ZetCode

Python time.perf_counter 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 time.perf_counter 函数,该函数为基准测试提供最高的可用分辨率计时器。我们将涵盖性能测量、计时比较和实际示例。

基本定义

time.perf_counter 函数以小数秒为单位返回高分辨率性能计数器值。它专为测量短持续时间而设计。

主要特征:最高的可用计时器分辨率、单调递增(始终增加)、不受系统时钟更改的影响,并且是基准测试的理想选择。参考点未定义,因此只有差异才有意义。

基本性能计时

time.perf_counter 的最简单用法是测量代码执行时间。此示例显示了计时函数调用的基本用法。

basic_timing.py
import time

def calculate_sum(n):
    return sum(range(n))

# Start timer
start = time.perf_counter()

# Execute function
result = calculate_sum(1000000)

# Stop timer
end = time.perf_counter()

# Calculate duration
duration = end - start
print(f"Calculation took {duration:.6f} seconds")
print(f"Result: {result}")

此示例演示如何测量函数的执行时间。perf_counter 提供适合性能测量的高分辨率计时。

:.6f 格式说明符以 6 位小数显示持续时间,以实现微秒精度。

比较多个实现

time.perf_counter 非常适合比较不同的实现。此示例比较了两种求和数字的方法。

compare_implementations.py
import time

def sum_with_loop(n):
    total = 0
    for i in range(n):
        total += i
    return total

def sum_with_builtin(n):
    return sum(range(n))

n = 1000000

# Time loop implementation
start = time.perf_counter()
result = sum_with_loop(n)
end = time.perf_counter()
print(f"Loop: {end - start:.6f} sec")

# Time built-in implementation
start = time.perf_counter()
result = sum_with_builtin(n)
end = time.perf_counter()
print(f"Built-in: {end - start:.6f} sec")

此模式有助于识别方法之间的性能差异。内置的 sum 通常比 Python 循环更快。

由于系统可变性,可能需要多次运行才能获得可靠的结果。

计时上下文管理器

上下文管理器提供了一种简洁的方法来计时代码块。此示例创建一个可重用的计时上下文。

timing_context.py
import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self
    
    def __exit__(self, *args):
        self.end = time.perf_counter()
        self.duration = self.end - self.start
        print(f"Execution took {self.duration:.6f} seconds")

# Using the context manager
with Timer():
    # Code to time
    data = [x**2 for x in range(10000)]
    filtered = [x for x in data if x % 2 == 0]
    total = sum(filtered)

print(f"Total: {total}")

上下文管理器自动处理计时启动/停止和输出。在对多个代码部分进行计时时,此模式减少了样板代码。

持续时间在块之后仍然可以作为 timer.duration 使用。

测量小代码段

perf_counter 擅长测量非常短的操作。此示例对单个列表操作进行计时。

micro_timing.py
import time

def time_operation(operation, n=1000):
    start = time.perf_counter()
    for _ in range(n):
        operation()
    end = time.perf_counter()
    return (end - start) / n

# Define operations
def list_append():
    lst = []
    lst.append(1)

def list_concat():
    lst = []
    lst = lst + [1]

# Time operations
append_time = time_operation(list_append)
concat_time = time_operation(list_concat)

print(f"Append: {append_time:.9f} sec/op")
print(f"Concat: {concat_time:.9f} sec/op")
print(f"Ratio: {concat_time/append_time:.1f}x")

通过重复操作和求平均值,我们可以测量非常短的持续时间。 这揭示了相似操作之间的性能差异。

:.9f 格式显示这些微小持续时间的纳秒精度。

与 time.time 比较

此示例演示了短间隔内 perf_countertime.time 之间的分辨率差异。

resolution_comparison.py
import time

def empty_function():
    pass

# Test with time.time
start = time.time()
empty_function()
end = time.time()
print(f"time.time resolution: {end - start:.9f} sec")

# Test with perf_counter
start = time.perf_counter()
empty_function()
end = time.perf_counter()
print(f"perf_counter resolution: {end - start:.9f} sec")

# Measure smallest detectable difference
def measure_resolution(func):
    min_diff = float('inf')
    for _ in range(100):
        start = func()
        end = func()
        diff = end - start
        if 0 < diff < min_diff:
            min_diff = diff
    return min_diff

print(f"time.time min resolution: {measure_resolution(time.time):.9f}")
print(f"perf_counter min resolution: {measure_resolution(time.perf_counter):.9f}")

perf_counter 通常显示比 time.time 高得多的分辨率,使其更适合微基准测试。

分辨率测试测量可检测到的最小非零计时差异。

多次运行的统计计时

对于可靠的基准测试,多次运行和统计分析有助于解决可变性。此示例显示了高级计时技术。

statistical_timing.py
import time
import statistics

def benchmark(func, n=1000):
    times = []
    for _ in range(n):
        start = time.perf_counter()
        func()
        end = time.perf_counter()
        times.append(end - start)
    
    mean = statistics.mean(times)
    stdev = statistics.stdev(times)
    minimum = min(times)
    maximum = max(times)
    
    print(f"Mean: {mean:.9f} sec")
    print(f"StdDev: {stdev:.9f} sec")
    print(f"Min: {minimum:.9f} sec")
    print(f"Max: {maximum:.9f} sec")
    print(f"Range: {maximum/minimum:.1f}x")

def test_function():
    sum(x*x for x in range(1000))

print("Benchmark results:")
benchmark(test_function)

与单次测量相比,此方法提供了更完整的性能特征图。它有助于识别可变性和异常值。

标准偏差等统计测量显示了跨运行的计时一致性。

最佳实践

资料来源

作者

我的名字是 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有 Python 教程