如何测量短段C/汇编代码的速度?
考虑以下C代码:如何测量短段C/汇编代码的速度?,c,linux,performance,assembly,C,Linux,Performance,Assembly,考虑以下C代码: #include <complex.h> complex float f(complex float x[]) { complex float p = 1.0; for (int i = 0; i < 32; i++) p += x[i]; return p; } gcc第7版(快照)带有-O3-march=core-avx2-ffast math给出: f: vmovups ymm1, YMMWORD PTR [rd
#include <complex.h>
complex float f(complex float x[]) {
complex float p = 1.0;
for (int i = 0; i < 32; i++)
p += x[i];
return p;
}
gcc第7版(快照)带有-O3-march=core-avx2-ffast math
给出:
f:
vmovups ymm1, YMMWORD PTR [rdi] #5.10
vmovups ymm2, YMMWORD PTR [64+rdi] #5.10
vmovups ymm5, YMMWORD PTR [128+rdi] #5.10
vmovups ymm6, YMMWORD PTR [192+rdi] #5.10
vmovsd xmm0, QWORD PTR p.152.0.0.1[rip] #3.19
vaddps ymm3, ymm1, YMMWORD PTR [32+rdi] #3.19
vaddps ymm4, ymm2, YMMWORD PTR [96+rdi] #3.19
vaddps ymm7, ymm5, YMMWORD PTR [160+rdi] #3.19
vaddps ymm8, ymm6, YMMWORD PTR [224+rdi] #3.19
vaddps ymm9, ymm3, ymm4 #3.19
vaddps ymm10, ymm7, ymm8 #3.19
vaddps ymm11, ymm9, ymm10 #3.19
vextractf128 xmm12, ymm11, 1 #3.19
vaddps xmm13, xmm11, xmm12 #3.19
vmovhlps xmm14, xmm13, xmm13 #3.19
vaddps xmm15, xmm13, xmm14 #3.19
vaddps xmm0, xmm15, xmm0 #3.19
vzeroupper #6.10
ret #6.10
f:
lea r10, [rsp+8]
and rsp, -32
push QWORD PTR [r10-8]
push rbp
mov rbp, rsp
push r10
vmovups ymm0, YMMWORD PTR [rdi+64]
vmovaps ymm1, YMMWORD PTR .LC0[rip]
vaddps ymm0, ymm0, YMMWORD PTR [rdi+32]
vaddps ymm1, ymm1, YMMWORD PTR [rdi]
vaddps ymm0, ymm0, ymm1
vmovups ymm1, YMMWORD PTR [rdi+128]
vaddps ymm1, ymm1, YMMWORD PTR [rdi+96]
vaddps ymm0, ymm0, ymm1
vmovups ymm1, YMMWORD PTR [rdi+192]
vaddps ymm1, ymm1, YMMWORD PTR [rdi+160]
vaddps ymm0, ymm0, ymm1
vaddps ymm0, ymm0, YMMWORD PTR [rdi+224]
vunpckhps xmm3, xmm0, xmm0
vshufps xmm2, xmm0, xmm0, 255
vshufps xmm1, xmm0, xmm0, 85
vaddss xmm1, xmm2, xmm1
vaddss xmm3, xmm3, xmm0
vextractf128 xmm0, ymm0, 0x1
vunpckhps xmm4, xmm0, xmm0
vshufps xmm2, xmm0, xmm0, 85
vaddss xmm4, xmm4, xmm0
vshufps xmm0, xmm0, xmm0, 255
vaddss xmm0, xmm2, xmm0
vaddss xmm3, xmm3, xmm4
vaddss xmm1, xmm1, xmm0
vmovss DWORD PTR [rbp-24], xmm3
vmovss DWORD PTR [rbp-20], xmm1
vzeroupper
vmovq xmm0, QWORD PTR [rbp-24]
pop r10
pop rbp
lea rsp, [r10-8]
ret
我对哪一个更快感兴趣,所以测量运行时间会很好
但是,我不知道如何度量花费这么少时间的代码的运行时间
哪种代码更快?如何可靠地测量它
您需要一个能够多次调用此函数的测试线束 这将使运行时达到一个非常重要的级别,并将平均操作系统调度引起的任何差异
void test_f()
{
complex float x[32] = { 1+2i, 2+3i }; // add as many as needed.
// here i is a special
// constant for complex numbers
int i;
for (i=0; i<10000000; i++) {
f(x);
}
}
void test_f()
{
复数浮点x[32]={1+2i,2+3i};//根据需要添加任意数量的浮点。
//这里我是一个特殊的
//复数常数
int i;
对于(i=0;i重复次数足够多,需要足够长的时间。太短会使其更容易出现轻微的计时异常(测量时间的开销,指令在时间上跳跃,通过OoOE将指令读入或读出有效计时区域,等等),太长。除非你是一个纯粹主义者,否则这并不重要。你通常可以猜测实际时间应该是多少,由于中断等原因,测量的时间会稍微高一点,但在调整时钟速度后,你应该会得到一个“接近”合理值的结果(例如,对于延迟测量,您应该获得整数个周期)。执行多次运行并绘制它,忽略奇怪的异常值,尤其是在顶部
确保您处于turbo模式(或在BIOS设置中禁用所有频率缩放),并且矢量单元在计时之前处于“唤醒”(对于AVX代码)状态,因此请进行一些预热。您打算计时的代码也可以这样做
请非常小心,您有意选择使迭代依赖(测量延迟)或独立(测量吞吐量),不要只是做一些随机的事情-你会有一个你不知道的度量值,即延迟或吞吐量。也不要让编译器优化它,将对你正在度量的东西的一些或所有调用都去掉,这样你就没有度量值了
您可以使用rdtscp
进行计时本身,或者使用精度较低的东西-精度越低,计时循环需要的时间越长。您可以从绘图的外观大致判断精度,如果它看起来高度离散,并且所有东西都排列在几个“箱子”中,请使用更多的迭代(或更好的时间度量)
如果您打算在特定的缓存条件下进行测量,那么它会变得更加棘手,因为设置该状态也需要时间,因此它会有点像“猜测开销”(很难精确测量)的游戏
FWIW ICC asm看起来更快,GCC正在进行大量的标量计算。无论何时,只要您想要执行性能度量,都需要执行以下几个步骤:
经常重复要测量的操作,以达到您可以精确测量的次数。通常一到十秒就足够了。在几乎所有情况下,都可以通过简单的循环来实现
防止优化器由于未使用的结果而优化重复或完全优化测量的操作
对此,有几种可能的方法:
- 在每次迭代中修改输入并实际使用所有结果。在您的情况下,这可能类似于:
complex float accu = 0+0i;
for(int i = 0; i < 100000000; i++) {
x[i%32] += 42+3i; //different input on each pass
accu += f(x);
}
printComplex(accu); //this depends on the output of all passes
优化器不会消除循环吗?对于gcc,您可以使用
\uuuuuu属性(优化(0)))
,以防止对测试函数进行优化。或者,您将测试函数放在使用不同优化设置编译的不同.c文件中。如果您关闭优化,则不会得到程序集,除非我有误解。请明确说明其他解决方案的工作方式好吗?@elean或者将测试线束放入一个文件harnese.c
,并将要测试的函数放入一个文件function.c
。如果愿意,可以使用-O3
编译这两个文件。然后将结果链接在一起而不进行优化。这样,优化器就看不到调用f(x)的情况
在测试线束中多次没有意义,不会优化掉回路。但是,它会优化f()
尽可能好。@cmaster听起来不错。你能用一些命令行示例添加一个答案,让我接受吗?如果你只是重复它,如何避免优化器消除循环?@eleanora你可以将函数放在不同的编译单元中(然后不使用LTO)(然后必须在循环中调用它,因为优化器必须假设函数可能会做一些有趣的事情),或者只是在汇编中编写循环,并确保它完全符合您的要求。您是否能够显示这些想法所需的代码?如果可以在汇编中添加循环,看起来是最好的解决方案,但我不知道如何混合C和汇编。@eleanora我可以展示一些示例,但这取决于平台的详细信息(还有汇编器)。不过,将C和汇编结合起来并不难,只要将/assembly编译成对象文件,并像往常一样用链接器将它们链接在一起即可。从汇编中调用C函数需要正确的名称(可能需要在函数名称前加下划线)和某种形式的extern
声明(汇编程序之间的语法不同)谢谢。我在linux上,很高兴使用对您最方便的任何工具。我认为我粘贴的程序集应该使用gas
编译/assembe。您使用这个库:如何使用完全优化编译,但不使用优化链接?您只需在编译时使用gcc-O3
,然后使用gcc
when链接。链接器通常只将编译过程中创建的符号视为黑匣子。但是
complex float f(complex float x[]) {
complex float p = 1.0;
return p;
}