当我在一些原始图像处理操作中使用SIMD指令本质的实验时,我正在构建一个微基准来测量性能变化。然而,编写有用的微基准是困难的,所以我想首先理解(如果可能的话)尽可能多的变化和错误的来源。
我必须考虑的一个因素是度量代码本身的开销。我正在使用RDTSC进行度量,并使用以下代码查找度量开销:
extern inline unsigned long long __attribute__((always_inline)) rdtsc64() {
unsigned int hi, lo;
__asm__ __volatile__(
"xorl %%eax, %%eax\n\t"
"cpuid\n\t"
"rdtsc"
: "=a"(lo), "=d"(hi)
: /* no inputs */
: "rbx", "rcx");
return ((unsigned long long)hi << 32ull) | (unsigned long long)lo;
}
unsigned int find_rdtsc_overhead() {
const int trials = 1000000;
std::vector<unsigned long long> times;
times.resize(trials, 0.0);
for (int i = 0; i < trials; ++i) {
unsigned long long t_begin = rdtsc64();
unsigned long long t_end = rdtsc64();
times[i] = (t_end - t_begin);
}
// print frequencies of cycle counts
}在运行此代码时,输出如下所示:
Frequency of occurrence (for 1000000 trials):
234 cycles (counted 28 times)
243 cycles (counted 875703 times)
252 cycles (counted 124194 times)
261 cycles (counted 37 times)
270 cycles (counted 2 times)
693 cycles (counted 1 times)
1611 cycles (counted 1 times)
1665 cycles (counted 1 times)
... (a bunch of larger times each only seen once)我的问题是:
的,这是什么非常不寻常的情况?
进一步信息
平台:
SpeedStep已关闭(处理器设置为性能模式,运行在2.4GHz);如果以“按需”模式运行,则在243和252个周期出现两个峰值,在360和369个周期出现两个(可能对应的)峰值。
我使用sched_setaffinity将进程锁定在一个核心上。如果我依次在每个核心上运行测试(即锁定到核心0并运行,然后锁定到核心1并运行),则两个核心的结果相似,只不过234个周期中最快的时间在核心1上发生的次数比在核心0上的次数要少一些。
编译命令是:
g++ -Wall -mssse3 -mtune=core2 -O3 -o test.bin test.cppGCC为核心循环生成的代码是:
.L105:
#APP
# 27 "test.cpp" 1
xorl %eax, %eax
cpuid
rdtsc
# 0 "" 2
#NO_APP
movl %edx, %ebp
movl %eax, %edi
#APP
# 27 "test.cpp" 1
xorl %eax, %eax
cpuid
rdtsc
# 0 "" 2
#NO_APP
salq $32, %rdx
salq $32, %rbp
mov %eax, %eax
mov %edi, %edi
orq %rax, %rdx
orq %rdi, %rbp
subq %rbp, %rdx
movq %rdx, (%r8,%rsi)
addq $8, %rsi
cmpq $8000000, %rsi
jne .L105发布于 2011-06-21 22:39:56
RDTSC可以返回不一致的结果,原因如下:
在一些CPU(特别是某些较旧的Opterons)上,
sched_setaffinity来处理这个问题了--很好!times[]的写操作)在速度上可能有所不同。在这种情况下,您很幸运,所使用的std::vector实现只是一个平面数组;即使如此,写入也会抛出一些东西。这可能是这段代码最重要的因素。对于Core2微体系结构,我还不足以确切地说明为什么要获得这个双峰分发版,也不足以说明您的代码如何以28次的速度运行,但这可能与上述原因之一有关。
发布于 2011-12-14 08:46:30
英特尔程序员手册建议您使用lfence;rdtsc或rdtscp,如果您希望确保rdtsc之前的指令已经实际执行。这是因为rdtsc本身并不是一个序列化指令。
发布于 2011-08-17 12:49:45
您应该确保在OS级别禁用频率节流/绿色功能。重新启动机器。否则,可能会出现核心具有不同步时间戳计数器值的情况。
到目前为止,243阅读是最常见的,这也是使用它的原因之一。另一方面,假设您经过的时间<243:您减去开销,得到一个下流。由于算法没有签名,所以最终得到了一个巨大的结果。这个事实说明使用最低读数(234)代替。要精确测量只有几个周期长的序列是非常困难的。在一个典型的x86 @几个GHz上,我建议不要使用小于10 be的定时序列,即使在这个长度上,它们通常也远离岩石。
我其余的回答是我做什么,我如何处理结果和我对主题的推理。
至于开销,最简单的方法是使用这样的代码
unsigned __int64 rdtsc_inline (void);
unsigned __int64 rdtsc_function (void);第一个表单将rdtsc指令发送到生成的代码中(如您的代码)。第二个将导致调用函数、执行rdtsc和返回指令。也许它会生成堆栈帧。显然,第二种形式要比第一种慢得多。
然后编写用于计算开销的(C)代码。
unsigned __int64 start_cycle,end_cycle; /* place these @ the module level*/
unsigned __int64 overhead;
/* place this code inside a function */
start_cycle=rdtsc_inline();
end_cycle=rdtsc_inline();
overhead=end_cycle-start_cycle;如果您使用内联变体,您将获得较低的开销(呃)。您还将面临计算比“应该”更大的开销的风险(特别是对于函数形式),这反过来意味着,如果您测量非常短/快速的序列,您可能会遇到以前计算的开销大于度量本身的开销。当你试图调整你的开销,你会得到一个下流,这将导致混乱的情况。处理这件事的最好方法是
我之前已经讨论过如何处理this thread中的结果。
我要做的另一件事是将度量代码集成到应用程序中。开销是微不足道的。在计算结果之后,我将其发送到一个特殊的结构中,在该结构中,我计算测量的数量、和x和x^2值,并确定最小和最大测量值。稍后,我可以使用数据来计算平均值和标准差。该结构本身是索引的,我可以测量不同的性能方面,例如单独的应用程序功能(“功能性能”),在cpu中花费的时间,磁盘读写,网络读写(“非功能性能”)等等。
如果应用程序从一开始就以这种方式进行检测和监视,我预计它在其生命周期内出现性能问题的风险将大大降低。
https://stackoverflow.com/questions/6432669
复制相似问题