使用程序集加快一点测试操作

use*_*213 3 c++ compiler-construction performance assembly compiler-optimization

我有一个性能问题 - 我无法击败编译器生成代码的发布版本速度.它慢了25%.我写的函数在我的测试中大约被调用了2000万次,所以让它运行得更快就能获得回报.

C++中的代码非常简单:

static inline char GetBit(char *data, size_t bit)
{ 
    return 0 != (data[bit / 8] & (1 << (bit % 8))); 
}
Run Code Online (Sandbox Code Playgroud)

这是我为64位MASM编写的版本:

mov   rax, rdx  
mov   r10, 8h
xor   rdx, rdx  
div   rax, r10  
mov   al, byte ptr [rax+rcx]  
mov   bl, 1h
mov   cl, dl  
shl   bl, cl  
and   al, bl  
shr   al, cl  
ret 
Run Code Online (Sandbox Code Playgroud)

好吧,我不是一个汇编人员,但我不认为编译器可以使代码更快25%,只是创建更好的汇编.所以诀窍是[可能]在函数调用中.它尊重C++代码的inline关键字并且不生成调用,但我无法使其适用于asm代码:

extern "C" inline char GetBitAsm(char *data, size_t bit);
Run Code Online (Sandbox Code Playgroud)

我使用dumpbin反汇编代码,我可以清楚地看到我的代码+函数调用.虽然没有为编译器的版本生成调用:

mov   rdx, qword ptr [bit]  
mov   rcx, qword ptr [data]  
call  GetBitAsm (013F588EFDh)  
mov   byte ptr [isbit], al  
Run Code Online (Sandbox Code Playgroud)

还有2个读数和一个写入内存,而在编译器生成的内容中,可能只有1个读数.我读到某处div操作代码需要大约20个周期,而单个内存访问至少需要100个周期.所以从内存中删除mov rdx和mov rcx,用父函数中的寄存器中的值替换它们,我认为会做的伎俩

问题:

  1. 这真的是它运行得如此之慢的原因吗?

  2. 如何在发布版本中使用asm inline编写函数?

  3. 如何进一步增强汇编代码,使其更快?

Ros*_*dge 5

相对于任何编译器的内联代码,函数调用听到和汇编代码中的DIV指令将破坏性能.单独的函数调用开销可能会大于编译器代码平均所需的周期数.DIV指令可能要高出几倍.

内存访问在现代处理器上通常是免费的,因为它们可以从处理器的缓存中得到满足.在您的汇编版本中,您的内存访问平均需要花费0个周期,因为您的代码可能足够慢,以至于处理器可以在需要访问内存之前轻松地将内存预取到其缓存中.另一方面,编译器的代码可能足够快,以至于它可以比处理器可以更快地从内存中读取值.它必须定期停止等待提取编译.因此,虽然编译器代码中的内存访问周期时间平均会更高,但这只是因为它更好地进行了优化.

解决问题的最佳方法是让编译器进行优化.坦率地说,它似乎知道如何生成比你更好的代码.即使是汇编专家也很难改进编译器,并且需要在更广泛的范围内查看问题,而不仅仅是指令选择这一功能.

如果你仍然使用自己的汇编代码,那么使用编译器的内联汇编功能,并摆脱DIV指令.它仍然不会像编译器的版本那样好,但是应该让它更接近它.