我一直在阅读div和mul组装操作,我决定通过在C中编写一个简单的程序来实现它们:
#include <stdlib.h>
#include <stdio.h>
int main()
{
size_t i = 9;
size_t j = i / 5;
printf("%zu\n",j);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
然后生成汇编语言代码:
gcc -S division.c -O0 -masm=intel
Run Code Online (Sandbox Code Playgroud)
但是看生成的division.s文件,它不包含任何div操作!相反,它通过位移和魔术数字来做某种黑魔法.这是一个计算代码片段i/5:
mov rax, QWORD PTR [rbp-16] ; Move i (=9) to RAX
movabs rdx, -3689348814741910323 ; Move some magic number to RDX (?)
mul rdx ; Multiply 9 by magic number
mov rax, rdx ; Take only the upper 64 bits of the …Run Code Online (Sandbox Code Playgroud) 来源我的回答:
我有点不在这里,我试图了解这种特殊优化是如何工作的.
正如答案中提到的,gcc将整数除法优化为7:
mov edx, -1840700269
mov eax, edi
imul edx
lea eax, [rdx+rdi]
sar eax, 2
sar edi, 31
sub eax, edi
Run Code Online (Sandbox Code Playgroud)
其转换为C为:
int32_t divideBySeven(int32_t num) {
int32_t temp = ((int64_t)num * -015555555555) >> 32;
temp = (temp + num) >> 2;
return (temp - (num >> 31));
}
Run Code Online (Sandbox Code Playgroud)
我们来看看第一部分:
int32_t temp = ((int64_t)num * -015555555555) >> 32;
Run Code Online (Sandbox Code Playgroud)
为什么这个号码?
那么,让我们取2 ^ 64并将其除以7,看看弹出的是什么.
2^64 / 7 = 2635249153387078802.28571428571428571429
Run Code Online (Sandbox Code Playgroud)
这看起来像一团糟,如果我们把它转换成八进制怎么办?
0222222222222222222222.22222222222222222222222
Run Code Online (Sandbox Code Playgroud)
这是一个非常漂亮的重复模式,当然这不是巧合.我的意思是我们记得7是0b111,我们知道当我们除以99时,我们倾向于在基数10中得到重复模式.因此,当我们除以7时,我们在基数8中得到重复模式是有道理的.
那么我们的号码在哪里?
(int32_t)-1840700269 是相同的 (uint_32t)2454267027
* 7 …
algorithm math optimization integer-division compiler-optimization