为什么标准的“abs”功能比我的快?

Lys*_*sol 13 c++ gcc g++ compiler-optimization

我想尝试制作自己的绝对值函数。我认为计算绝对值的最快方法是简单地屏蔽符号位(IEEE 754 中的最后一位)。我想将它的速度与标准abs功能进行比较。这是我的实现:

// Union used for type punning
union float_uint_u
{
    float f_val;
    unsigned int ui_val;
};

// 'MASK' has all bits == 1 except the last one
constexpr unsigned int MASK = ~(1 << (sizeof(int) * 8 - 1));

float abs_bitwise(float value)
{
    float_uint_u ret;
    ret.f_val = value;
    ret.ui_val &= MASK;
       
    return ret.f_val;
}
Run Code Online (Sandbox Code Playgroud)

作为记录,我知道这种类型的双关语不是标准的 C++。但是,这仅用于教育目的,根据文档,这在 GCC 中得到支持

我认为这应该是计算绝对值的最快方法,因此它至少应该与标准实现一样快。但是,对随机值的 100000000 次迭代计时,我得到了以下结果:

Bitwise time: 5.47385 | STL time: 5.15662
Ratio: 1.06152
Run Code Online (Sandbox Code Playgroud)

我的abs函数慢了大约 6%。

组装输出

我使用-O2优化和-S选项(程序集输出)进行编译,以帮助确定发生了什么。我已经提取了相关部分:

; 16(%rsp) is a value obtained from standard input
movss   16(%rsp), %xmm0
andps   .LC5(%rip), %xmm0 ; .LC5 == 2147483647
movq    %rbp, %rdi
cvtss2sd    %xmm0, %xmm0

movl    16(%rsp), %eax
movq    %rbp, %rdi
andl    $2147483647, %eax
movd    %eax, %xmm0
cvtss2sd    %xmm0, %xmm0
Run Code Online (Sandbox Code Playgroud)

观察

我不擅长汇编,但我注意到的主要事情是标准函数直接在xmm0寄存器上运行。但是对于我的,它首先将值移动到eax(出于某种原因),执行and,然后将其移动到xmm0. 我假设额外的mov是减速发生的地方。我还注意到,对于标准,它将位掩码存储在程序中的其他位置而不是立即数。不过,我猜这并不重要。这两个版本也使用不同的指令(例如movlvs movss)。

系统信息

这是在 Debian Linux(不稳定分支)上用 g++ 编译的。g++ --version输出:

g++ (Debian 10.2.1-6) 10.2.1 20210110
Run Code Online (Sandbox Code Playgroud)

如果这两个版本的代码都以相同的方式(通过and)计算绝对值,为什么优化器不会生成相同的代码?具体来说,为什么mov在优化我的实现时感觉需要包含额外内容?

Dan*_*ica 6

我得到了一个有点不同的组件。根据 x86_64 Linux ABI,float参数通过xmm0. 使用 standard fabsAND直接在这个寄存器上执行按位运算(Intel 语法):

andps xmm0, XMMWORD PTR .LC0[rip] # .LC0 contains 0x7FFFFFFF
ret
Run Code Online (Sandbox Code Playgroud)

但是,在您的情况下,按位AND对类型为 的对象执行unsigned int。因此,GCC 做同样的事情,需要先移动xmm0eax

movd eax, xmm0
and  eax, 2147483647
movd xmm0, eax
ret
Run Code Online (Sandbox Code Playgroud)

现场演示:https : //godbolt.org/z/xj8MMo

我还没有找到任何方法来强制GCC优化器AND直接执行xmm0仅使用纯 C/C++ 源代码。似乎高效的实现需要建立在汇编代码或 Intel 内在代码之上。

相关问题:如何对浮点数进行按位运算。所有提出的解决方案基本上都会产生相同的结果。

我也尝试使用该copysign功能,但结果更糟。生成的机器代码然后包含 x87 指令。


无论如何,很有趣的是,Clang优化器足够聪明,可以使所有 3 种情况下的程序集等效:https : //godbolt.org/z/b6Khv5