“定制内在”可以使用x64而不是内联汇编功能吗?

我目前正在尝试为我的图书馆创建高度优化,可重复使用的功能。例如,我通过以下方式编写函数“is power of 2”:
template<class IntType>  
inline bool is_power_of_two( const IntType x )
{
    return (x != 0) && ((x & (x - 1)) == 0);
}
这是一个可移植,低维护的实现,作为内联C ++模板。此代码由VC ++ 2008编译为具有分支的以下代码:
is_power_of_two PROC
    test    rcx, rcx
    je  SHORT $LN3@is_power_o
    lea rax, QWORD PTR [rcx-1]
    test    rax, rcx
    jne SHORT $LN3@is_power_o
    mov al, 1
    ret 0
$LN3@is_power_o:
    xor al, al
    ret 0
is_power_of_two ENDP
我从这里找到了实现:“bit twiddler”,它将在x64的程序集中编码,如下所示:
is_power_of_two_fast PROC
    test rcx, rcx
    je  SHORT NotAPowerOfTwo
    lea rax, [rcx-1]
    and rax, rcx
    neg rax
    sbb rax, rax
    inc rax
    ret
NotAPowerOfTwo:
    xor rax, rax
    ret
is_power_of_two_fast ENDP
我测试了在汇编模块(.asm文件)中与C ++分开编写的两个子例程,第二个工作速度提高了约20%! 然而,函数调用的开销是相当大的:如果我将第二个程序集实现“is_power_of_two_fast”与模板函数的内联版本进行比较,后者尽管分支更快! 不幸的是,x64的新约定指定不允许内联汇编。应该使用“内在功能”。 现在问题是:我可以将更快的版本“is_power_of_two_fast”实现为自定义内部函数或类似的东西,以便它可以内联使用吗?或者,是否有可能以某种方式强制编译器生成函数的低分支版本?     
已邀请:
甚至VC 2005也能够使用sbb指令生成代码。 用于C代码
bool __declspec(noinline) IsPowOf2(unsigned int a)
{
    return (a>=1)&((a&(a-1))<1);
}
编译如下
00401000  lea         eax,[ecx-1] 
00401003  and         eax,ecx 
00401005  cmp         eax,1 
00401008  sbb         eax,eax 
0040100A  neg         eax  
0040100C  cmp         ecx,1 
0040100F  sbb         ecx,ecx 
00401011  add         ecx,1 
00401014  and         eax,ecx 
00401016  ret          
    
不,你不能实现任何自定义内在函数,它们都内置在编译器中。它不仅是内置的指令,而且编译器也知道内在的语义,并为不同的周围代码调整代码。 为x86-64删除内联汇编的一个原因是将汇编插入到函数的中间会干扰优化器,并且通常会导致围绕汇编代码的优化代码不太好。那里很容易出现净亏损! 内在函数的唯一真正用途是编译器无法从C或C ++构造(如BSF或BSR)生成的“有趣”特殊指令。其他大部分内容都可以使用内联函数更好地工作,比如上面的模板。 如果您需要做一些特殊的事情,编译器不理解,唯一真正的选择是将整个函数编写为单独的汇编程序模块。如果该函数的调用开销太昂贵,那么优化可能首先不值那么多。 相信你的编译器(tm)!     
在这个简单的案例中,VC10 x64内在函数不会有很大帮助。 您拥有的动态分支是由&amp;&amp;和运营商,这是一个早期的运营商。 在许多情况下(你的情况是一个完美的例子),最好通过计算所有分支的结果来避免分支,然后应用掩码来选择好的分支。带掩码的cpp代码如下所示:
template<typename T_Type>
inline bool isPowerOfTwo(T_Type const& x)
{
    // static type checking for the example
    static_assert( std::is_integral<T_Type>::value && std::is_unsigned<T_Type>::value, "limited to unsigned types for the example" );
    typedef std::make_signed<T_Type>::type s_Type;

    // same as yours but with no branching
    return bool(  ((s_Type( s_Type(x != 0) << (s_Type(sizeof(T_Type)<<3u)-1) )) >> (s_Type(s_Type(sizeof(T_Type)<<3u)-1)))  & ((x & (x - 1)) == 0)  );
}
在上面的代码中,我没有检查签名类型的数字是否为负数。再次,一个简单的掩码将通过向右(numBit-1)次执行算术移位来获得该技巧,以获得负数的(~0)值和正数的0。     
前进的唯一方法是退一步,开始查看更大的图片。要么停止实施微优化API,要么在MASM64,YASM,NASM等中优化所有优化的API调用。 如果你使用一个更强大的汇编程序,你可以将小函数转换为宏,所以基本上将基于内联汇编程序函数的C / C ++头文件更改为汇编程序包含文件。     

要回复问题请先登录注册