perf_counter 比 time.time 更适合测函数耗时,因其返回单调递增的高精度计时器值,不受系统时钟调整、睡眠或调度干扰,且分辨率可达纳秒级,是 python 官方推荐的短时间间隔测量工具。

perf_counter 为什么比 time.time 更适合测函数耗时
time.perf_counter 返回的是单调递增的高精度计时器值,不受系统时钟调整(如 NTP 同步、手动改时间)影响,也不受睡眠或进程调度干扰。而 time.time 返回的是挂钟时间,一旦系统时间被修改,两次调用差值可能为负或严重失真。
它底层通常映射到硬件计数器(如 TSC),在多数平台分辨率可达纳秒级(实际精度取决于 OS 和 CPU),是 Python 官方推荐的“测量短时间间隔”的唯一可靠选择。
正确用法:必须成对调用且避免干扰
最简但易错的写法是直接在函数前后各调一次 perf_counter,但要注意:
- 两次调用必须在同一线程中,跨线程无意义(Python 的 GIL 不保证跨线程计时一致性)
- 不要把函数调用和计时器混在同一行,例如
start = perf_counter(); func()—— 这会把func的返回值赋给start,导致逻辑错误 - 若测试对象含 I/O 或异步操作,需确保整个执行路径被包裹,不能只包同步部分
推荐写法:
from time import perf_counter <p>start = perf_counter() result = my_function(*args, **kwargs) end = perf_counter() elapsed = end - start # 单位:秒,float 类型 </p>
常见误用:循环测多次时忘记重置或取均值
单次测量受 GC、CPU 频率波动、缓存预热等影响大,真实性能评估需要多次运行。但很多人直接写:
for _ in range(100):
start = perf_counter()
my_function()
print(perf_counter() - start)
这会产生两个问题:
- 每次循环都重新触发 Python 解释器开销(如字节码加载、局部变量创建),污染纯函数耗时
- 未排除异常值,也未考虑 warm-up 效应(前几次慢,后面变快)
更稳妥的做法是:先跑 3–5 次预热,再正式运行 10–100 次并取 min()(排除 GC / 中断干扰)或 median()(抗异常值):
# 预热
for _ in range(3):
my_function()
<h1>正式采集</h1><p>times = []
for _ in range(50):
start = perf_counter()
my_function()
times.append(perf_counter() - start)</p><p>print(f"Best: {min(times):.6f}s")
</p>
对比 timeit 时该选哪个
如果你只是想快速比对两段代码的相对性能,timeit 模块更省心——它自动处理预热、禁用 GC、重复执行、取最小值。但它的黑盒特性让你无法在函数内部插桩、无法捕获中间状态、也无法和业务逻辑耦合(比如测某次 API 调用 + 数据解析的总耗时)。
而 perf_counter 是白盒工具,你完全控制起点和终点。只要记住一点:timeit 适合“微基准测试”,perf_counter 适合“真实路径耗时定位”。别用 timeit 去测一个含网络请求的函数——它默认禁用 GC,反而会让内存行为失真。
真正难的不是调用 perf_counter,而是确定哪两行之间才代表你要测的“有效区间”。比如函数里调了 requests.get,你是想测整个 HTTP 往返,还是只测响应体解析?这个边界得你自己划清楚。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











