sorted()在百万级数据上变慢是因为timsort单线程执行,无法利用多核cpu,实测200万整数耗时0.8秒;并行分治方案通过multiprocessing.pool分块排序+heapq.merge归并可提速至0.3秒,关键在于避免资源闲置与内存峰值。

为什么 sorted() 在百万级数据上变慢了
因为 sorted() 默认用 Timsort,单线程执行——哪怕你有 16 核 CPU,它也只用 1 个核。实测在 200 万整数列表上,sorted() 耗时约 0.8 秒;而并行分治方案可压到 0.3 秒左右,提速近 3 倍。
这不是算法复杂度问题(都是 O(n log n)),而是资源闲置:Timsort 没法自动并行,也没法跳过已有序段。
- 数据完全在内存中(比如
list或array.array),但长度 ≥ 50 万 → 并行分块 +heapq.merge是最简可行路径 - 数据来自文件或数据库流式加载 → 别硬塞进内存,该用外部排序就用
sort -S或pandas.read_csv(..., chunksize=)+ 分批归并 - 数据含大量重复值或局部有序 → 单纯换算法没用,得先检测 runs(自然有序段),再做 natural merge
用 multiprocessing.Pool 实现安全分块排序
别直接用 Process 手动管理,开销大且易出错;Pool 能复用进程、控制并发数,关键是避免小块导致调度反超收益。
关键参数要调准:
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
-
chunk_size = max(10000, len(data) // (os.cpu_count() or 4))—— 太小(如 100)会让进程启动/通信成本盖过排序收益 - 传入
initializer避免每次子进程重复 import —— 尤其用numpy时,否则每个子进程都初始化一次 NumPy 环境 - 别用
map_async后直接get()等结果,要用imap流式消费,减少内存峰值
示例核心逻辑:
from multiprocessing import Pool
import heapq
def sort_chunk(chunk):
return sorted(chunk)
def parallel_sort(data, chunk_size=50000):
chunks = [data[i:i+chunk_size] for i in range(0, len(data), chunk_size)]
with Pool() as pool:
sorted_chunks = pool.imap(sort_chunk, chunks)
return list(heapq.merge(*sorted_chunks))
归并阶段用 heapq.merge 而不是 sorted() 合并
如果你把各块排好后用 sorted(sum(sorted_chunks, [])),等于又做了一次全量排序,白忙活。正确做法是利用各块已有序的特性,用 heapq.merge 在 O(n) 时间内完成合并。
-
heapq.merge是惰性迭代器,不一次性加载所有数据到内存 —— 对内存敏感场景很关键 - 它要求每个输入可迭代对象本身已升序,否则行为未定义(不会报错,但结果错)
- 如果块数特别多(> 100),
heapq.merge的堆操作常数会上升,此时应两两合并(类似归并树),而非一股脑喂进去
何时该放弃手写,直接用 pandas 或 numpy
当你的数据不是纯 Python list,而是带类型或结构的(比如数值列、混合 dtype、含 NaN),硬套 sorted() 或自写归并反而更慢、更易错。
- 纯数值?用
numpy.sort(arr, axis=0, kind='mergesort')—— 底层 C 实现 + 可选并行(需编译支持 OpenMP) - 带列名/索引/缺失值?用
df.sort_values('col', kind='mergesort')—— pandas 内部已对 chunk 和 memory layout 做了深度优化 - 排序后还要 groupby / agg?别先 sort 再操作,用
df.groupby(...).apply(lambda x: x.sort_values(...))很可能触发重复排序,直接用df.sort_values(...).groupby(...)
真正容易被忽略的点:并行排序不是“越多进程越好”,而是要匹配 I/O 能力和内存带宽。在 NVMe SSD + 64GB 内存机器上,开 8 进程比开 32 进程快;但在老式 SATA 盘上,并行太多反而因磁盘争抢变慢。实操前,先用 timeit 跑不同 chunk_size 和 processes 组合,别凭感觉调。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










