一个支持多线程并行计算、专门为Python 3.13+设计的 Map-Reduce 工具库。基于 Python 3.13 里新出的“无 GIL(Global Interpreter Lock,全局解释器锁)”特性;它主要用来解决 CPU 密集型的任务,让 Python 终于能真正发挥多核 CPU 的性能。
它是一个专门为 Python 3.13 及以上 自由线程(free-threaded)版本 写的 Map-Reduce 库。以前 Python 因为 GIL 限制,多线程没法真正利用 CPU 并行,CPU 密集任务还是只能靠单核。现在这个库能让 Python 在多核上“真并行”,所以跑一些计算密集型的任务,比如数值计算、基因数据处理,就能提速。
以前 Python 因为 GIL 限制,CPU 密集型任务(比如算素数、矩阵运算)多线程跑不快,反而还可能更慢。
Python 3.13 出了一个新特性——无 GIL 版本(free-threaded Python),终于能让多线程真正并行用满多核 CPU。
作者基于这个写了一个 Map-Reduce 库,你只需要传:
- map 函数(每个数据要做的事),
- reduce 函数(怎么合并结果),
- 输入数据,
- 开几个线程 + 每批数据大小,
性能测试表明:它确实能加速,但因为线程切换、迭代器安全、硬件差异等原因,效率没法达到理论最优。不过整体比单线程快很多,特别适合生物信息学、数学计算这种大任务。
但要注意:
- 必须用 Python 3.13 free-threaded 版本才有意义。
- 老版本 Python(有 GIL 的)对 CPU 任务开线程没用,甚至更慢。
- I/O 密集型任务在老版本多线程仍然有意义。
- 有些库(比如 pandas、gzip)会偷偷把 GIL 再打开,导致性能掉回单核,要小心。
这是个能在 Python 3.13 无 GIL 环境下,用 Map-Reduce 方式真多核跑计算的工具,能加速但不是“完美加速”,未来还有优化空间。
代码案例
python
from threaded_map_reduce import map_reduce
from operator import add
# 例子:用 4 个线程算 1 到 1000 的平方和
def square(x):
return x * x
result = map_reduce(
map_fn=square, # 每个元素要执行的函数
reduce_fn=add, # 怎么把结果合并(这里是相加)
iterable=range(1, 1001), # 输入数据
num_computing_threads=4, # 开多少个线程
num_items_per_chunk=100 # 每次线程拿多少数据一批处理
)
print(f"Sum of squares: {result}")
用起来跟写普通 map-reduce 差不多,但背后是多线程跑的。
作者平时做生物信息学、基因相关的研究,计算量很大,很多任务都喜欢用 Map-Reduce 写。
所以他想:既然 Python 3.13 开始能自由多线程了,那我干脆写个 Map-Reduce 库,输入可以直接是 iterable,然后丢给线程去跑。
参数解释
* map\_fn:对每个输入数据做的处理函数。
* reduce\_fn:怎么把结果合并。注意必须是可结合的(比如加法、乘法这种,先算谁后算谁结果一样)。
* iterable:输入的数据集合。
* num\_computing\_threads:要开几个线程来跑。
* num\_items\_per\_chunk:每次线程拿多少个数据一起处理。
返回值就是最终合并的结果。
性能测试
作者拿计算“100 万以内的素数个数”做测试。
* 测试机器是 i7-1260P(有 16 线程:4 大核 + 8 小核)。
* 理想情况:假设单核算要 16 秒,那 16 线程并行就应该缩短到 1 秒。
* 实际情况:没那么美好,因为多线程调度和数据切分都有开销,即使只用一个线程也会有额外负担。
所以结果是:多线程确实比单线程快,但没有达到理论最优。
为什么没那么高效?
1. 迭代器不是线程安全的
多个线程一起从迭代器取数据会出问题(甚至直接报错)。所以必须加锁或者换方案。
加锁会让线程等待彼此,性能下降。
2. CPU 硬件限制
作者的 CPU 里只有 4 个大核,剩下都是效率低的小核,所以本来就达不到理论最大速度。
3. GIL 还会偷偷回来
虽然 Python 3.13 支持无 GIL,但一旦你 import 一些 C 库(比如 pandas、gzip),Python 会默认它们不是线程安全的,于是又会自动开回 GIL。这样一来,多线程计算就等于退回单核跑了。
所以你要是发现明明开了多线程,CPU 占用还是一个核,很可能是这个问题。
作者的尝试过程
* 最初 naive 版本:用锁保护迭代器,让多线程安全取数据 → 性能很差,因为锁竞争太激烈。
* 改进一:用 队列 代替锁。队列本身是线程安全的,每个线程从队列取任务 → 好很多。
* 改进二:把任务分块(chunk),一次给线程一批数据,而不是一个一个取 → 性能更好。
* 最终方案:只用分块(不用队列),简单又高效。
使用建议
* 适用场景:CPU 密集型任务 + Python 3.13 free-threaded 版本。
* 不适合:老版本 Python(有 GIL 的),因为那样多线程对 CPU 任务反而更慢。
* 但注意:如果是 I/O 密集型(比如网络请求、文件读写),老 Python 的多线程还是有用的。
结论
这个库能让 Python 真正利用多核 CPU 来跑 Map-Reduce,虽然性能还没到理想情况,但已经比单线程好多了。