ZetCode

Python time.monotonic_ns 函数

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

本综合指南探讨了 Python 的 time.monotonic_ns 函数,该函数返回一个纳秒级分辨率的单调时钟值。我们将介绍精确计时、性能测量和实际示例。

基本定义

time.monotonic_ns 函数以纳秒为单位返回一个时钟值。它是单调的(永不减少)并且不受系统时钟调整的影响。

主要特点:纳秒级分辨率,非常适合精确的计时间隔,并保证永远不会倒退。参考点未定义,但在程序执行期间是一致的。

monotonic_ns 的基本用法

此示例演示了 time.monotonic_ns 的基本用法,用于测量具有纳秒精度的计时间隔。

basic_monotonic.py
import time

# Get current monotonic time in nanoseconds
start = time.monotonic_ns()

# Simulate some work
time.sleep(0.5)  # Sleep for 500 milliseconds

# Get end time
end = time.monotonic_ns()

# Calculate duration in nanoseconds
duration_ns = end - start
duration_ms = duration_ns / 1_000_000  # Convert to milliseconds

print(f"Operation took {duration_ns} nanoseconds")
print(f"Which is {duration_ms:.2f} milliseconds")

此示例展示了如何以纳秒精度测量时间间隔。即使系统时钟发生变化,睡眠持续时间也能被准确测量。

请注意,虽然分辨率为纳秒,但实际精度取决于底层系统能力。

比较 monotonic_ns 和 monotonic

此示例将 time.monotonic_nstime.monotonic 进行比较,以显示精度和表示形式的差异。

comparison.py
import time

def test_function():
    sum(range(1_000_000))

# Using time.monotonic()
start = time.monotonic()
test_function()
end = time.monotonic()
print(f"time.monotonic(): {(end - start) * 1e9:.0f} ns")

# Using time.monotonic_ns()
start = time.monotonic_ns()
test_function()
end = time.monotonic_ns()
print(f"time.monotonic_ns(): {end - start} ns")

这两个函数使用相同的底层时钟,但 monotonic_ns 提供对纳秒值的直接访问,无需浮点转换。

对于非常精确的测量,monotonic_ns 版本避免了潜在的浮点精度问题。

使用 monotonic_ns 进行微基准测试

此示例演示了如何使用 time.monotonic_ns 对具有高精度的小代码段进行微基准测试。

microbenchmark.py
import time

def benchmark(func, iterations=1_000_000):
    start = time.monotonic_ns()
    for _ in range(iterations):
        func()
    end = time.monotonic_ns()
    ns_per_op = (end - start) / iterations
    print(f"{func.__name__}: {ns_per_op:.2f} ns/operation")

def empty_function():
    pass

def simple_math():
    42 * 42

benchmark(empty_function)
benchmark(simple_math)

此模式对于测量非常小的操作的性能非常有用。纳秒分辨率允许对单个操作进行精确计时。

请注意,微基准测试可能会受到各种系统因素的影响,应谨慎解释。

精确的速率限制

此示例使用 time.monotonic_ns 实现了一个高精度速率限制器,以纳秒级的精度控制操作频率。

rate_limiter.py
import time

class NanoRateLimiter:
    def __init__(self, operations_per_second):
        self.interval_ns = 1_000_000_000 // operations_per_second
        self.last_run = 0
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            now = time.monotonic_ns()
            elapsed = now - self.last_run
            if elapsed < self.interval_ns:
                delay_ns = self.interval_ns - elapsed
                time.sleep(delay_ns / 1e9)  # Convert to seconds
            self.last_run = time.monotonic_ns()
            return func(*args, **kwargs)
        return wrapper

@NanoRateLimiter(1000)  # 1000 operations per second
def high_freq_operation():
    print("Operation at", time.monotonic_ns() % 1_000_000)

for _ in range(5):
    high_freq_operation()

速率限制器通过精确测量纳秒级的时间间隔,确保操作不会超过指定的速率。

这对于硬件控制或高频 API 尤其有用,在这些情况下,精确计时至关重要。

测量小代码块

此示例展示了如何使用 time.monotonic_ns 和上下文管理器来方便地测量非常小的代码块。

timing_context.py
import time
from contextlib import contextmanager

@contextmanager
def time_ns():
    start = time.monotonic_ns()
    yield
    end = time.monotonic_ns()
    print(f"Duration: {end - start} ns")

# Measure list comprehension
with time_ns():
    squares = [x*x for x in range(1000)]

# Measure dictionary creation
with time_ns():
    d = {x: x*x for x in range(1000)}

上下文管理器自动测量并打印 with 语句中代码块的执行时间。

这种模式可以很容易地将精确计时添加到现有代码中,而不会用计时代码使逻辑混乱。

长时间运行的进程监控

此示例演示了如何使用 time.monotonic_ns 来监控长时间运行的进程,并定期更新进度。

progress_monitor.py
import time

def long_running_task():
    total_items = 1_000_000
    start_time = time.monotonic_ns()
    last_report = start_time
    
    for i in range(total_items):
        # Simulate work
        time.sleep(0.001)
        
        # Report progress every second
        current_time = time.monotonic_ns()
        if current_time - last_report >= 1_000_000_000:
            elapsed_s = (current_time - start_time) / 1_000_000_000
            progress = (i + 1) / total_items * 100
            print(f"{elapsed_s:.1f}s: {progress:.1f}% complete")
            last_report = current_time
    
    total_time = (time.monotonic_ns() - start_time) / 1_000_000_000
    print(f"Task completed in {total_time:.2f} seconds")

long_running_task()

该示例使用纳秒级计时来精确控制进度报告的时间间隔,同时避免浮点累积误差造成的漂移。

这种方法确保即使对于非常长时间运行的进程,计时也是准确的,因为很小的计时误差可能会累积得非常显著。

最佳实践

资料来源

作者

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

列出所有 Python 教程