利用多核CPU来跑Map-Reduce的Python开源工具


一个支持多线程并行计算、专门为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,虽然性能还没到理想情况,但已经比单线程好多了。