c++ - 使用汇编加速位测试操作

标签 c++ performance assembly compiler-construction compiler-optimization

我有一个性能问题 - 我根本无法超越编译器生成代码的发布版本速度。它慢了 25%。我编写的函数在我的测试中被调用了大约 2000 万次,因此让它运行得更快会有返回。

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

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

这是我为 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 

好吧,我不是一个汇编专家,但我不认为编译器仅仅创建更好的汇编就可以使代码速度提高 25%。所以诀窍[可能]在函数调用中。它尊重 C++ 代码的内联关键字并且不生成任何调用,但我就是不能让它为 asm 代码工作:

extern "C" inline char GetBitAsm(char *data, size_t bit);

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

mov   rdx, qword ptr [bit]  
mov   rcx, qword ptr [data]  
call  GetBitAsm (013F588EFDh)  
mov   byte ptr [isbit], al  

另外还有2次读取和1次写入内存,而在编译器生成的内容中,可能只有1次读取。我在某处读到 div 操作码大约需要 20 个周期,而单个内存访问至少需要 100 个周期。因此,从内存中删除 mov rdx 和 mov rcx,用父函数中寄存器中的值替换它们,我认为,可以解决问题

问题:

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

  2. 如何在 release 版本中使用 asm 编写的函数内联?

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

最佳答案

相对于任何编译器的内联代码,无意中听到的函数调用和汇编代码中的 DIV 指令会降低性能。仅函数调用开销就可能大于编译器代码平均花费的周期数。 DIV 指令可能会大几倍。

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

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

如果您仍然宁愿使用自己的汇编代码,那么可以使用编译器的内联汇编功能,并摆脱 DIV 指令。它的性能仍然不如编译器的版本,但应该更接近。

关于c++ - 使用汇编加速位测试操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24723158/

相关文章:

c++ - 推力 device_vector resize 上的 cuda 未对齐地址

c++ - 如何获取 std::numeric_limits<char>::min() 值?

windows - 我可以获得 Windows Azure 诊断性能计数器名称的完整列表吗?

c - 不需要的跳转中断 MPLAB X IDE v.3.30

c++ - 对链表感到困惑

c++ - R Rcpp big.matrix加入

Windows 上的 Python。为什么读取文本比二进制文件慢 8 倍?

c# - c#/perfmon 中的自定义性能计数器

macos - OS X Lion 上的 68k 汇编器

考虑下面的源代码,其中 N 是用 #define 声明的常量