C++ 优化器:除法
C++ Optimizer : division
假设我有 2 个 if
语句:
if (frequency1_mhz > frequency2_hz * 1000) {// some code}
if (frequency1_mhz / 1000 > frequency2_hz ) {// some code}
我想这两者的功能完全相同,但我猜第一个乘法语句比除法更有效。
C++ 编译器会对此进行优化吗?或者这是我在设计代码时应该考虑的事情
不,这些不是等效的状态机,因为除法不是浮点数或整数乘法的精确逆运算。
- 整数除法向下舍入正分数
int f1=999;
int f2=0;
static_assert(f1>f2*1000);
static_assert(f1/1000==f2);
- 倒数不精确:
static_assert(10.0!=10*(1.0/10));
是也不是。
- 代码不相同:
- 由于四舍五入,结果可能存在差异(例如
frequency1_mhz=1001
和 frequency2_hz=1
)
- 第一个版本可能比第二个版本更快溢出。例如1000000000 的
frequency2_hz
会溢出 int
(并导致 UB)
- 仍然可以使用乘法来执行除法。
不确定时,请查看生成的程序集。
Here's 为两个版本生成的程序集。第二个更长,但仍然没有分割。
version1(int, int):
imul esi, esi, 1000
xor eax, eax
cmp esi, edi
setl al
ret
version2(int, int):
movsx rax, edi
imul rax, rax, 274877907 ; look ma, no idiv!
sar edi, 31
sar rax, 38
sub eax, edi
cmp eax, esi
setg al
movzx eax, al
ret
如果它们是用 -O3
构建的浮点数,GCC 将生成相同的程序集(无论好坏)。
bool first(float frequency1_mhz,float frequency2_hz) {
return frequency1_mhz > frequency2_hz * 1000;
}
bool second(float frequency1_mhz,float frequency2_hz) {
return frequency1_mhz / 1000 > frequency2_hz;
}
first(float, float):
mulss xmm1, DWORD PTR .LC0[rip]
comiss xmm0, xmm1
seta al
ret
second(float, float):
divss xmm0, DWORD PTR .LC0[rip]
comiss xmm0, xmm1
seta al
ret
.LC0:
.long 1148846080
所以,实际上,它的代码相同:-)
假设我有 2 个 if
语句:
if (frequency1_mhz > frequency2_hz * 1000) {// some code}
if (frequency1_mhz / 1000 > frequency2_hz ) {// some code}
我想这两者的功能完全相同,但我猜第一个乘法语句比除法更有效。
C++ 编译器会对此进行优化吗?或者这是我在设计代码时应该考虑的事情
不,这些不是等效的状态机,因为除法不是浮点数或整数乘法的精确逆运算。
- 整数除法向下舍入正分数
int f1=999;
int f2=0;
static_assert(f1>f2*1000);
static_assert(f1/1000==f2);
- 倒数不精确:
static_assert(10.0!=10*(1.0/10));
是也不是。
- 代码不相同:
- 由于四舍五入,结果可能存在差异(例如
frequency1_mhz=1001
和frequency2_hz=1
) - 第一个版本可能比第二个版本更快溢出。例如1000000000 的
frequency2_hz
会溢出int
(并导致 UB)
- 仍然可以使用乘法来执行除法。
不确定时,请查看生成的程序集。
Here's 为两个版本生成的程序集。第二个更长,但仍然没有分割。
version1(int, int):
imul esi, esi, 1000
xor eax, eax
cmp esi, edi
setl al
ret
version2(int, int):
movsx rax, edi
imul rax, rax, 274877907 ; look ma, no idiv!
sar edi, 31
sar rax, 38
sub eax, edi
cmp eax, esi
setg al
movzx eax, al
ret
如果它们是用 -O3
构建的浮点数,GCC 将生成相同的程序集(无论好坏)。
bool first(float frequency1_mhz,float frequency2_hz) {
return frequency1_mhz > frequency2_hz * 1000;
}
bool second(float frequency1_mhz,float frequency2_hz) {
return frequency1_mhz / 1000 > frequency2_hz;
}
first(float, float):
mulss xmm1, DWORD PTR .LC0[rip]
comiss xmm0, xmm1
seta al
ret
second(float, float):
divss xmm0, DWORD PTR .LC0[rip]
comiss xmm0, xmm1
seta al
ret
.LC0:
.long 1148846080
所以,实际上,它的代码相同:-)