EDIT:原来这种奇怪的行为只发生在我的WSL ubuntu中的python上。否则,序列式确实比多线程式运行得更快。
我知道,对于CPython来说,通常情况下,多个线程只是在使用同一个CPU核心的情况下进行上下文切换,而不是像启动python解释器的多个实例那样使用多个CPU核心。
我知道,如果做得好的话,多线程对I/O绑定的任务很有好处。尽管如此,使用多线程时,CPU限制的任务实际上会更慢。因此,我用3个代码片段进行了实验,每个代码片段都进行了一些CPU限制的计算。
- 示例1:按顺序运行任务(单线程(
- 示例2:在不同线程中运行每个任务(多线程(
- 示例3:在单独的进程中运行每个任务(多进程(
令我惊讶的是,尽管任务受CPU限制,但使用多个线程的示例2的执行速度(平均1.5秒(比使用单线程的示例1(平均2.2秒(更快。但是示例3的运行速度与预期的一样快(平均1秒(。
我不知道我做错了什么。
示例1:按顺序运行任务
import time
import math
nums = [ 8, 7, 8, 5, 8]
def some_computation(n):
counter = 0
for i in range(int(math.pow(n,n))):
counter += 1
if __name__ == '__main__':
start = time.time()
for i in nums:
some_computation(i)
end = time.time()
print("Total time of program execution : ", round(end-start, 4) )
示例2:使用多线程运行任务
import threading
import time
import math
nums = [ 8, 7, 8, 5, 8]
def some_computation(n):
counter = 0
for i in range(int(math.pow(n,n))):
counter += 1
if __name__ == '__main__':
start = time.time()
threads = []
for i in nums:
x = threading.Thread(target=some_computation, args=(i,))
threads.append(x)
x.start()
for t in threads:
t.join()
end = time.time()
print("Total time of program execution : ", round(end-start, 4) )
示例3:与multiprocessing
模块并行运行任务
from multiprocessing import Pool
import time
import math
nums = [ 8, 7, 8, 5, 8]
def some_computation(n):
counter = 0
for i in range(int(math.pow(n,n))):
counter += 1
if __name__ == '__main__':
start = time.time()
pool = Pool(processes=3)
for i in nums:
pool.apply_async(some_computation, [i])
pool.close()
pool.join()
end = time.time()
print("Total time of program execution : ", round(end-start, 4) )
原来这只发生在我安装在Windows Subsystem for Linux中的ubuntu中。我的原始代码段在Windows或Ubuntu python环境中按预期运行,但在WSL中没有,即顺序执行比多线程执行运行得更快。感谢@Vlad再次检查您的情况。
正如我在评论中所说,这是一个函数实际在做什么的问题。
如果我们使nums列表更长(即,将有更多的并发线程/进程(,并调整循环范围的计算方式,那么我们会看到:
import time
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
nums = [8,7,8,5,8,8,5,4,8,7,7,8,8,7,8,8,8]
def some_computation(n):
counter = 0
for _ in range(n*1_000_000):
counter += 1
return counter
def sequential():
for n in nums:
some_computation(n)
def threaded():
with ThreadPoolExecutor() as executor:
executor.map(some_computation, nums)
def pooled():
with ProcessPoolExecutor() as executor:
executor.map(some_computation, nums)
if __name__ == '__main__':
for func in sequential, threaded, pooled:
start = time.perf_counter()
func()
end = time.perf_counter()
print(func.__name__, f'{end-start:.4f}')
输出:
sequential 4.8998
threaded 5.1257
pooled 0.7760
这表明some_computation((的复杂性决定了系统的行为方式。有了这段代码及其调整后的参数,我们可以看到线程处理比按顺序运行慢(正如人们通常预期的那样(,当然,多处理也比快得多