我知道我必须使用:rdtsc。测量的函数是确定性的,但结果远非可重复(我从运行到运行得到5%的振荡)。 可能的原因是:
你知道其他原因吗? 如何消除它们?
答案 0 :(得分:5)
TSC( rdtsc 使用的)通常在多处理器系统上不同步。设置CPU亲和性以将进程绑定到单个CPU可能会有所帮助。
您还可以从HPET timers获取时间戳(如果可用),这些时间戳不会出现同样的问题。
至于重复性,这些差异是正确的。您可以禁用缓存,为进程提供实时优先级和/或(如果在Linux或类似的情况下)使用较低的固定定时器中断频率(执行时间分片的那个)重新编译内核。你不能完全消除差异,至少不容易消除,而不是常规的CPU + OS组合。
一般而言,为了便于编码,可靠性和便携性,我建议您使用操作系统提供的功能。如果它提供高精度计时器,请使用适当的OS助手。
(以防万一你正在尝试对加密系统进行时间攻击,好吧,你将不得不忍受1.这种随机性和2.一般防御使得系统因各种原因无法预测,所以关于时间,函数可能不是确定性的。)
编辑:添加了关于操作系统可以提供的计时器的段落。
编辑:这是指Linux。要将进程绑定到单个CPU(从RDTSC获得准确的读取),可以使用sched_setaffinity(2)。而here是我的一个项目中的一些代码,它们将其用于其他目的(将线程映射到CPU)。这应该是你的第一次尝试。对于HPET,只要内核和机器支持这些定时器,就可以使用these之类的常规POSIX调用。
答案 1 :(得分:2)
请参阅问题Is stopwatch benchmarking acceptable?,了解现代多核多线程多进程机器上微基准测试的差异。
虽然问题与Java有关,但这些注意事项适用于任何语言的基准测试。
另见:How do I write a correct micro-benchmark in Java?
另见:What advice can you give me for writing a meaningful benchmark?
答案 2 :(得分:2)
为什么要消除它们?听起来你已经创造了一个现实的基准。在野外使用时,该代码也具有相同的可变性。可能更糟,因为您可能已经消除了磁盘和CPU缓存延迟。使用Jon Skeet的方法,创造条件,给你最好的结果,只会给你一个让你感觉良好但永远无法实现的结果。
如果绝对数字很重要,请计算中位数,而不是平均数。
答案 3 :(得分:2)
实际上在较新的Linux内核中有新的perf子系统。例如:
$ ./perf stat du -s /tmp 94796 /tmp Performance counter stats for 'du -s /tmp': 2.546403 task-clock-msecs # 0.060 CPUs 3 context-switches # 0.001 M/sec 0 CPU-migrations # 0.000 M/sec 166 page-faults # 0.065 M/sec 2434963 cycles # 956.236 M/sec 1798092 instructions # 0.738 IPC 302969 branches # 118.979 M/sec 26197 branch-misses # 8.647 % 23217 cache-references # 9.118 M/sec 4621 cache-misses # 1.815 M/sec 0.042406580 seconds time elapsed
答案 4 :(得分:1)
添加到原因列表:分支预测/误预测(这可以通过在某些芯片上具有复杂预测缓存的上下文切换来实现。此外,预测可能受到程序的不同输入的影响,因此直接比较时间两个不同的数据集可能略有偏差。
一般来说,几乎不可能减轻所有这些,但是你可以做些事情来帮助每一个:
但是,当然,到目前为止,做这样的计时的最佳方法是在非常大的数据上多次执行这些操作,以便最大限度地减少由您无法控制的事物引入的可变性。 (它永远不会被删除。)
答案 5 :(得分:0)
大多数现代处理器都支持一组卓越的低级硬件性能计数器。 如果您真的想知道答案,包括对缓存未命中和上下文切换开销的实际测量,请抓住PAPI (Performance API) toolkit,然后,在某些(尽管不是全部)操作系统上安装一个内核补丁,并且需要额外的努力,你已经开始跑步了。