【发布时间】:2021-05-07 10:36:29
【问题描述】:
我想尝试制作自己的绝对值函数。我认为计算绝对值的最快方法是简单地屏蔽符号位(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;
}
为了记录,我知道这种类型的双关语不是标准的 C++。但是,这仅用于教育目的,根据文档,this is supported in GCC。
我认为这应该是计算绝对值的最快方法,因此它至少应该与标准实现一样快。但是,对随机值的 100000000 次迭代计时,我得到了以下结果:
Bitwise time: 5.47385 | STL time: 5.15662
Ratio: 1.06152
我的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
观察
我不擅长汇编,但我注意到的主要一点是标准函数直接在xmm0 寄存器上运行。但是对于我的,它首先将值移动到eax(出于某种原因),执行and,然后将其移动到xmm0。我假设额外的mov 是减速发生的地方。我还注意到,对于标准,它将位掩码存储在程序的其他位置而不是立即数。不过,我猜这并不重要。这两个版本也使用不同的指令(例如movl vs movss)。
系统信息
这是在 Debian Linux(不稳定分支)上使用 g++ 编译的。 g++ --version 输出:
g++ (Debian 10.2.1-6) 10.2.1 20210110
如果这两个版本的代码都以相同的方式计算绝对值(通过and),为什么优化器不生成相同的代码?具体来说,为什么它在优化我的实现时感觉需要包含一个额外的mov?
【问题讨论】:
-
不知道为什么,但在 MSVC 中,您的实现在调试中要快得多,但在发行版中是一样的。
-
@D-RAJ 在调试中进行性能比较是没有意义的。他们可能在调试模式下激活了一些在发布版本中不存在的检查。
-
你试过内联函数吗?
-
@WolfgangLorenz 如果您的意思是
inline关键字,那么您应该研究一下inline现在的含义。inline如果您在头文件中定义一个函数并在多个编译单元中使用该头文件,那么inline很重要。但是对于现代编译器来说,inline关键字本身并不会改变它是否内联函数的概率。 -
你为什么操作系统编译? ABI 很奇怪。
标签: c++ gcc g++ compiler-optimization