bigywater.tech
Python · 25

timeit 的陷阱:为什么简单测速经常不准确

用timeit对同一段代码分别做number=1的单次测速和number=100000,repeat=5的多轮测速,看单次测速有多不靠谱,正确用法该怎么设置参数。

3.4倍波动
number=1连续3次的最大差距
<3%波动
number=100000,repeat=5的稳定性
3.1纳秒
空循环本身的固有开销

做的事:只测一次,看的抖动有多大

最常见的timeit误用就是number=1,觉得"跑一次看看多快"就够了。

代码import timeit for _ in range(3): print(timeit.timeit("sum(range(100))", number=1))

验证:同一段代码,number=1连续测3次差了3倍多

代码完全没变,环境也没有人为制造干扰。

结果single-run(number=1) samples: 1.70909333974123e-06 7.080379873514175e-07 5.00003807246685e-07

三次结果是1.71微秒、0.71微秒、0.50微秒——最大值和最小值差了3.4倍,全部来自操作系统调度、CPU缓存状态、Python解释器内部状态这些存在但你控制不了的噪声,单次耗时本身短到跟这些噪声在同一个量级,噪声完全淹没了信号。

验证:提高number并用repeat多轮测速,数字稳定了

timeit推荐的用法是repeat=多轮,每轮number设置得足够大再取最小值。

结果timeit.repeat("sum(range(100))", number=100000, repeat=5) → [0.02786, 0.02806, 0.02817, 0.02839, 0.02846]

这5轮结果彼此只相差不到3%,远比number=1的3.4倍波动稳定得多——number=100000把单次运行的耗时(约0.28微秒/次)放大到能跟噪声区分开的量级,repeat=5取多轮里的最小值,则是为了排除"某一轮恰好被系统调度打断"这类偶发的额外干扰,官方文档也建议用min()而不是mean(),因为额外的干扰只会让某一轮变慢,不会让某一轮变得比速度更快。

验证:空循环本身也有的固定开销

用一个什么都不做的pass语句测100万次循环。

结果timeit.timeit("pass", number=1000000) = 0.0031秒 换算成单次:约3.1纳秒/次
这3.1纳秒是timeit测量框架本身"跑一次循环体"的固有开销(不含被测代码),当被测代码本身也只有几纳秒到几十纳秒时,这份固有开销占的比例就不能忽略。这也是为什么timeit要求你尽量提高number——number越大,被测代码占的总耗时相对框架开销就越大,测出来的数字才越接近被测代码的速度,而不是被"测量本身的成本"稀释掉。

分步演示:timeit该怎么用

每一步都是本文上面用timeit跑出来的原始结果。

number=1的单次测速被噪声淹没
这一步新验证的测速方式

    本文用到的工具

    • 本机 Python 3.14.7
    • timeit.timeit / timeit.repeat:对比number=1和大number+repeat的稳定性差异
    京ICP备2026067502号-1京公网安备11010502063487号