You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何打印整数列表的最快方式如此违背直觉?

批量打印整数列表的性能分析与疑问

我有一个整数列表ints,希望以最快速度将每个整数逐行打印。

初始实现为:

list(map(print,ints))

在处理10⁷个整数(从标准输入读取)时,30次重复测试的平均耗时为1708ms。

之前看到一篇认可度较高的Stack Overflow回答提到,用list()展开map结果会因创建大量无意义的None列表而低效,这符合直觉。于是我尝试了多种实现方式:

# n次print调用
list(map(print,ints))                         # map
for n in ints: print(n)                       # loop
for i in range(0,len(ints)): print(ints[i])   # range
[print(n) for n in ints]                      # compr
deque(map(print,ints),0)                      # deque

# 单次print调用
print('\n'.join(map(str,ints)))               # cat
print(*ints, sep='\n')                        # sep

结合ints = list(map(int,sys.stdin.readlines()))测试,各方法耗时如下:

map    1707 ms
loop   1926 ms
range  2027 ms
compr  1828 ms
deque  1699 ms
--------------
cat    1084 ms
sep    1462 ms

这些结果并非偶然,但实际情况完全违背直觉:创建千万级None列表的map变体,反而比其他n次print调用的变体快得多,Python是否优化了该列表的创建?而最令人惊讶的是,创建超大字符串的cat变体速度最快——本以为内存分配会严重影响性能,即便默认flush=False。

核心问题

为何map变体比其他n次print调用的变体快得多?
为何cat变体的速度还要更快?

示例代码(map.py)

#!/usr/bin/python3

import sys

if __name__ == '__main__':
  ints = list(map(int,sys.stdin.readlines()))
  list(map(print,ints))

运行方式

/usr/bin/time -f '%e' -o map.run-time -a ./map.py < input.log | wc -l > /dev/null

问题解答

1. 为什么map变体比其他n次print调用更快?

  • 底层实现差异:map是Python内置的C语言实现迭代器,它调用print的过程在C层面完成,相比Python层面的for循环(包括普通loop、列表推导),减少了大量Python字节码的执行开销——普通for循环每次迭代都要处理变量绑定、循环指令等Python级逻辑,额外开销远高于C级迭代。
  • 列表创建的高效性:虽然list(map(...))会生成全为None的大列表,但这个列表的创建是在C层面批量完成的,内存分配和元素填充的效率远高于Python层面逐个添加元素的操作(比如列表推导[print(n) for n in ints])。而deque(map(...), 0)只是消费迭代器不保存结果,耗时和map接近,进一步说明map本身的迭代效率是关键。
  • 额外操作的开销:range版本需要额外做索引查找ints[i],比直接遍历元素的loop多了一层开销,所以速度最慢;列表推导本质还是Python级循环,只是语法更紧凑,开销略低于普通loop,但仍高于map。

2. 为什么cat变体的速度最快?

  • IO次数的大幅减少:系统IO是性能瓶颈的核心。n次print调用即使开启缓冲,最终也会触发多次IO刷写(缓冲有大小限制);而cat变体只触发单次系统IO操作,把所有内容一次性写入,IO的耗时占比远高于内存操作,减少IO次数能直接大幅提升性能。
  • 字符串拼接的高效性:'\n'.join(map(str,ints))的字符串拼接是Python优化过的操作——join会预先计算所有字符串的总长度,一次性分配内存完成拼接,避免了多次字符串拷贝的开销。相比多次调用str()和print()的累计成本,单次大内存分配的代价可以忽略。
  • sep版本的额外开销:print(*ints, sep='\n')虽然也是单次print,但它需要在Python层面逐个将整数转换为字符串,并处理分隔符拼接,这个过程的效率远低于join的C级字符串拼接,所以耗时比cat高。

内容的提问来源于stack exchange,提问作者bitmask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 17:07:11