为何单线程多线程Python代码比纯单线程快?多线程性能为何下降?
Python多线程求和的性能疑惑
测试代码与结果
单线程实现
import time import random large_list = [random.randint(-100, 100) for _ in range(200000000)] start_time = time.time() total = 0 for n in large_list: total += n print(total) print(time.time() - start_time)
运行输出:
-1153397 # total sum 9.442509889602661 # time taken
多线程实现(可切换线程数)
from multiprocessing.pool import ThreadPool start_time = time.time() _num_threads = 10 total = 0 del chunks def partial_sum(chunk): total = 0 for n in chunk: total += n return total chunk_size = len(large_list) // _num_threads chunks = [large_list[i * chunk_size: (i + 1) * chunk_size] for i in range(_num_threads)] if len(large_list) % _num_threads != 0: chunks.append(large_list[_num_threads * chunk_size:]) # Calculate the partial sums in parallel with ThreadPool(processes=_num_threads) as pool: partial_sums = pool.map(partial_sum, chunks) total_sum = sum(partial_sums) print(total_sum) print(time.time() - start_time)
10线程运行结果(12核CPU):
-1153397 # total sum (expected to be around zero) 8.065398454666138 # time taken
1线程运行多线程代码结果:
-1153397 # total sum (same as before) 5.773534536361694 # time taken
疑问
- 为何多线程代码的单线程版本比纯单线程实现快得多?
- 为何在12核CPU上增加线程数到10时性能反而下降?
解答
问题1:单线程多线程版本更快的原因
核心在于CPU缓存命中率的提升。
纯单线程版本遍历的是2亿元素的超大列表,内存地址跨度极大,CPU的L1/L2缓存无法有效缓存后续要访问的数据,频繁出现缓存失效,需要从内存中读取数据,速度很慢。
而多线程版本(即使单线程运行)会先把大列表分割成多个连续的小chunk,每个chunk的内存是连续的,遍历小chunk时,CPU缓存可以一次性加载更多后续元素,缓存命中率大幅提升,内存访问效率更高,所以整体运行时间更短。
问题2:10线程性能下降的原因
这是Python的**GIL(全局解释器锁)**导致的:
CPython解释器有一个全局锁,同一时刻只能有一个线程执行Python字节码。对于求和这种纯CPU密集型任务,多线程无法真正并行执行,各个线程只能轮流获得GIL执行任务,反而会增加线程上下文切换、线程池调度的额外开销。
你的12核CPU无法被多线程版本利用起来,因为GIL限制了Python线程的并行能力,额外的线程只会带来调度成本,导致整体运行时间比单线程多线程版本更长。如果要真正利用多核CPU,应该使用multiprocessing(多进程)而非多线程,因为每个进程有独立的Python解释器和GIL,可以真正并行执行。
内容的提问来源于stack exchange,提问作者Héctor Balsells Roure
相关产品推荐
相关产品推荐

