Rhy*_*hys 9 python benchmarking multithreading multiprocess
在花了很多时间试图绕过多处理之后我想出了这个代码,这是一个基准测试:
例1:
from multiprocessing import Process
class Alter(Process):
def __init__(self, word):
Process.__init__(self)
self.word = word
self.word2 = ''
def run(self):
# Alter string + test processing speed
for i in range(80000):
self.word2 = self.word2 + self.word
if __name__=='__main__':
# Send a string to be altered
thread1 = Alter('foo')
thread2 = Alter('bar')
thread1.start()
thread2.start()
# wait for both to finish
thread1.join()
thread2.join()
print(thread1.word2)
print(thread2.word2)
Run Code Online (Sandbox Code Playgroud)
这在2秒内完成(多线程的一半).出于好奇,我决定下次运行:
例2:
word2 = 'foo'
word3 = 'bar'
word = 'foo'
for i in range(80000):
word2 = word2 + word
word = 'bar'
for i in range(80000):
word3 = word3 + word
print(word2)
print(word3)
Run Code Online (Sandbox Code Playgroud)
令我恐惧的是,这不到半秒!
这里发生了什么?我希望多处理运行得更快 - 不应该完成一半示例2的时间给出示例1是示例2分为两个进程?
在考虑了Chris的反馈之后,我已经包含了消耗最多处理时间的"实际"代码,并引导我考虑多处理:
self.ListVar = [[13379+ strings],[13379+ strings],
[13379+ strings],[13379+ strings]]
for b in range(len(self.ListVar)):
self.list1 = []
self.temp = []
for n in range(len(self.ListVar[b])):
if not self.ListVar[b][n] in self.temp:
self.list1.insert(n, self.ListVar[b][n] + '(' +
str(self.ListVar[b].count(self.ListVar[b][n])) +
')')
self.temp.insert(0, self.ListVar[b][n])
self.ListVar[b] = list(self.list1)
Run Code Online (Sandbox Code Playgroud)
Chr*_*rle 13
此示例太小,无法从多处理中受益.
启动新流程时会产生大量开销.如果涉及繁重的处理,那将是可以忽略的.但是你的例子真的不是那么密集,所以你必须注意到开销.
你可能会注意到与真正的线程有更大的区别,太糟糕的python(好吧,CPython)存在CPU绑定线程的问题.
Dav*_*son 12
ETA:现在您已经发布了代码,我可以告诉您,有一种简单的方法可以更快地完成您所做的事情(> 100倍速度).
我看到你正在做的是在括号中为字符串列表中的每个项添加一个频率.而不是每次都计算所有元素(正如您可以使用cProfile确认的那样,是目前代码中最大的瓶颈),您可以创建一个从每个元素映射到其频率的字典.这样,您只需要通过列表两次 - 一次创建频率字典,一次使用它来添加频率.
在这里,我将展示我的新方法,计算时间,并使用生成的测试用例将其与旧方法进行比较.测试用例甚至显示新结果与旧结果完全相同.注意:你真正需要注意的是new_method.
import random
import time
import collections
import cProfile
LIST_LEN = 14000
def timefunc(f):
t = time.time()
f()
return time.time() - t
def random_string(length=3):
"""Return a random string of given length"""
return "".join([chr(random.randint(65, 90)) for i in range(length)])
class Profiler:
def __init__(self):
self.original = [[random_string() for i in range(LIST_LEN)]
for j in range(4)]
def old_method(self):
self.ListVar = self.original[:]
for b in range(len(self.ListVar)):
self.list1 = []
self.temp = []
for n in range(len(self.ListVar[b])):
if not self.ListVar[b][n] in self.temp:
self.list1.insert(n, self.ListVar[b][n] + '(' + str(self.ListVar[b].count(self.ListVar[b][n])) + ')')
self.temp.insert(0, self.ListVar[b][n])
self.ListVar[b] = list(self.list1)
return self.ListVar
def new_method(self):
self.ListVar = self.original[:]
for i, inner_lst in enumerate(self.ListVar):
freq_dict = collections.defaultdict(int)
# create frequency dictionary
for e in inner_lst:
freq_dict[e] += 1
temp = set()
ret = []
for e in inner_lst:
if e not in temp:
ret.append(e + '(' + str(freq_dict[e]) + ')')
temp.add(e)
self.ListVar[i] = ret
return self.ListVar
def time_and_confirm(self):
"""
Time the old and new methods, and confirm they return the same value
"""
time_a = time.time()
l1 = self.old_method()
time_b = time.time()
l2 = self.new_method()
time_c = time.time()
# confirm that the two are the same
assert l1 == l2, "The old and new methods don't return the same value"
return time_b - time_a, time_c - time_b
p = Profiler()
print p.time_and_confirm()
Run Code Online (Sandbox Code Playgroud)
当我运行它时,它得到的时间为(15.963812112808228,0.05961179733276367),这意味着它快了约250倍,尽管这个优势取决于列表的长度和每个列表中的频率分布.我相信你会同意这个速度优势,你可能不需要使用多处理:)
(我的原始答案留在下面为后代)
ETA:顺便说一下,值得注意的是,这个算法在列表长度上大致是线性的,而你使用的代码是二次的.这意味着元素数量越多,它就越有利.例如,如果将每个列表的长度增加到1000000,则只需5秒即可运行.根据推断,旧代码将花费一天:)
这取决于您正在执行的操作.例如:
import time
NUM_RANGE = 100000000
from multiprocessing import Process
def timefunc(f):
t = time.time()
f()
return time.time() - t
def multi():
class MultiProcess(Process):
def __init__(self):
Process.__init__(self)
def run(self):
# Alter string + test processing speed
for i in xrange(NUM_RANGE):
a = 20 * 20
thread1 = MultiProcess()
thread2 = MultiProcess()
thread1.start()
thread2.start()
thread1.join()
thread2.join()
def single():
for i in xrange(NUM_RANGE):
a = 20 * 20
for i in xrange(NUM_RANGE):
a = 20 * 20
print timefunc(multi) / timefunc(single)
Run Code Online (Sandbox Code Playgroud)
在我的机器上,多处理操作仅占单线程操作时间的约60%.
mde*_*ous 12
多处理可能对您正在做的事情很有用,但不会影响您正在考虑使用它的方式.由于您基本上对列表的每个成员进行了一些计算,您可以使用该multiprocessing.Pool.map
方法,并行地对列表成员进行计算.
下面是一个示例,它使用单个进程显示代码的性能并使用multiprocessing.Pool.map
:
from multiprocessing import Pool
from random import choice
from string import printable
from time import time
def build_test_list():
# Builds a test list consisting of 5 sublists of 10000 strings each.
# each string is 20 characters long
testlist = [[], [], [], [], []]
for sublist in testlist:
for _ in xrange(10000):
sublist.append(''.join(choice(printable) for _ in xrange(20)))
return testlist
def process_list(l):
# the time-consuming code
result = []
tmp = []
for n in range(len(l)):
if l[n] not in tmp:
result.insert(n, l[n]+' ('+str(l.count(l[n]))+')')
tmp.insert(0, l[n])
return result
def single(l):
# process the test list elements using a single process
results = []
for sublist in l:
results.append(process_list(sublist))
return results
def multi(l):
# process the test list elements in parallel
pool = Pool()
results = pool.map(process_list, l)
return results
print "Building the test list..."
testlist = build_test_list()
print "Processing the test list using a single process..."
starttime = time()
singleresults = single(testlist)
singletime = time() - starttime
print "Processing the test list using multiple processes..."
starttime = time()
multiresults = multi(testlist)
multitime = time() - starttime
# make sure they both return the same thing
assert singleresults == multiresults
print "Single process: {0:.2f}sec".format(singletime)
print "Multiple processes: {0:.2f}sec".format(multitime)
Run Code Online (Sandbox Code Playgroud)
输出:
Building the test list...
Processing the test list using a single process...
Processing the test list using multiple processes...
Single process: 34.73sec
Multiple processes: 24.97sec
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
13549 次 |
最近记录: |