[英]Why is the sequential code faster than the multithreaded code?
为什么顺序代码比多线程代码运行得更快?
import time
import math
import threading
def run(i):
i = i * math.pi
return i**2
def run2(i, j, l):
for x in range(i, j):
l.append((x*math.pi)**2)
if __name__ == "__main__":
tm = time.time()
# Sequential
res = [run(i) for i in range(20000)]
print(time.time() - tm)
tm = time.time()
# Multithreaded
n = 24 # Number of threads
ans = []
t = 20000
for i in range(n - 1):
ans.append([])
tp = threading.Thread(target=run2, args=(round(t//n*i), round(t//n*(i+1)), ans[len(ans) - 1]))
tp.start()
ans.append([])
tp = threading.Thread(target=run2, args=(round(t//n*n), t, ans[len(ans) - 1]))
tp.start()
print(time.time() - tm)
似乎对于非密集型任务,顺序更快。 任何想法为什么以及如何加速多线程版本?
由于 GIL,python 解释器在任何给定时间都不能运行多个 python 代码。
但是, appending
,您要appending
到列表中。
>>> import timeit
>>> def appending():
... output = []
... for i in range(1000000):
... output.append(i)
... return output
...
>>> def gen_exp():
... return [i for i in range(1000000)]
>>> print(f"{timeit.timeit(appending, number=100):.2}")
8.1
>>> print(f"{timeit.timeit(gen_exp, number=100):.2}")
5.2
追加到list
这种缓慢性质在readline/readlines性能差异上得到了最好的展示。
如果没有这些,通常时间基准将被简化如下。
import math
import timeit
from concurrent import futures
import multiprocessing
def run(i):
i = i * math.pi
return i ** 2
def wrapper_sequential():
return [run(i) for i in range(20000)]
def wrapper_thread_pool():
with futures.ThreadPoolExecutor(max_workers=10) as exc:
fut = [exc.submit(run, i) for i in range(20000)]
output = [f.result() for f in fut]
return output
def wrapper_multiprocess():
with multiprocessing.Pool(10) as pool:
output = pool.map(run, (i for i in range(20000)))
return output
if __name__ == '__main__':
print(f"Thr: {timeit.timeit(wrapper_thread_pool, number=10):.4}")
print(f"Seq: {timeit.timeit(wrapper_sequential, number=10):.4}")
print(f"Mlt: {timeit.timeit(wrapper_multiprocess, number=10):.4}")
Thr: 5.146
Seq: 0.05411
Mlt: 4.055
创建thread
成本是不值得的,因为 GIL 在任何给定时刻只允许一个 python 解释器单个 python 代码。
对于Multiprocessing
,由于 python 解释器没有直接的方式通过进程进行通信,因此内部使用pickle
来序列化进程间通信的数据 - 这是开销。
如果计算量足够大, Multiprocessing
最终将克服该开销并开始领先于顺序处理,但thread
永远不会。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.