我正在编写一个c++基准测试程序,它涉及计时许多函数调用。这些函数被反复调用,每次调用都被记录下来,以便以后进行统计分析。它要求函数在多个线程上同时运行,因此为了保证基准测试的准确性和公平性,它在实时操作系统上运行,并控制调度行为。以下是我关心的问题:
是否有收集计时数据的确定方法?我看过printf和stringstream,但似乎都没有确定性的行为,由于内存&缓冲区操作。它们在O(1)中也没有表现出同样的原因,对吗?目前,我正在使用一个大的字符数组和一个自定义的strcat
函数,以便每个时间值可以在O(1)中收集。在测试结束时,当收集到所有数据时,将打印该数组。
我使用clock_gettime
计时和clock_getres
给我1ns的分辨率。这个值可信吗?
到目前为止,我做的事情是否正确?在编写基准测试时,我是否应该注意其他问题?
调用高频计时器并将样本写入输出流是获得性能数据的非常明智的方法。但是有一些棘手的问题需要注意。
- 事实上,你不应该使用printf和stringstream——不仅因为它们的执行时间是可变的,而且定义不清,还因为它们太慢了,特别是当你每微秒将你的perf数据格式化成字符串的时候!最好是将二进制数据写入预分配的缓冲区(如结构数组),然后在测试完成后对其进行格式化。这将更快,给你一个更一致的写开销。如果编写内核的人不是笨蛋,那么带有高分辨率计时器(例如
- clock_gettime应该是可靠的。如果您想直接查询CPU计时器,可以查看性能应用程序编程接口库,但这应该不是必需的。
- 多线程本质上是混乱的(在确定性意义上),因为线程相互争夺CPU缓存和内存带宽。您可以得到随机变化的结果,这取决于同时调度的线程是否碰巧接触到相同的内存,或者是否一直从数据缓存中驱逐彼此的工作——这将根据数据在内存中的布局方式以及正在运行的线程而有所不同。但这没关系:工程中的许多过程都是随机的。只要多次运行基准测试,就可以得到统计上显著的平均值和百分位数的偏差。
CLOCK_PROCESS_CPUTIME_ID
)的或者,如果您真的需要100%的确定性,您需要确保您的线程以相同的顺序调度,为相同的量运行,并在每次运行时将它们的数据放在相同的内存地址中。
是否不出于实际性能考虑而使用大0表示法?
话虽如此,对于问题的其余部分:
性能收集将花费一些时间(O(1)仍然是有意义的时间,只是它不依赖于您的数据)。你需要使它最有效。
这意味着:
-
不使用
printf
和喜欢,而是写入一个特殊的内存区域,稍后将从中提取数据。 -
出于同样的原因,不要使用
strcat
,而是使用二进制数据的struct
s。完成后再解析 -
考虑测量平均值而不是测量每个呼叫(即:不是测量每个呼叫,而是每个1000和平均值以提取单个呼叫的大致成本)。这将使您的测量开销减少。虽然这并不总是可能的,但考虑一下。
-
clock_gettime
通常是可以信任的,但这取决于你的操作系统和硬件-检查它们,有时硬件时钟分辨率可能不像你希望的那么小。