【发布时间】:2021-04-06 22:20:55
【问题描述】:
我发现在__int128 上手动计算% 运算符比内置编译器运算符要快得多。我将向您展示如何计算模 9,但该方法可用于计算任何其他数的模。
首先,考虑内置的编译器操作符:
uint64_t mod9_v1(unsigned __int128 n)
{
return n % 9;
}
现在考虑我的手动实现:
uint64_t mod9_v2(unsigned __int128 n)
{
uint64_t r = 0;
r += (uint32_t)(n);
r += (uint32_t)(n >> 32) * (uint64_t)4;
r += (uint32_t)(n >> 64) * (uint64_t)7;
r += (uint32_t)(n >> 96);
return r % 9;
}
测量超过 100,000,000 个随机数会得出以下结果:
mod9_v1 | 3.986052 secs
mod9_v2 | 1.814339 secs
在 AMD Ryzen Threadripper 2990WX 上使用了带有 -march=native -O3 的 GCC 9.3.0。
Here 是godbolt的链接。
我想问一下你的行为是否相同? (在向 GCC Bugzilla 报告错误之前)。
更新: 根据要求,我提供了一个生成的程序集:
mod9_v1:
sub rsp, 8
mov edx, 9
xor ecx, ecx
call __umodti3
add rsp, 8
ret
mod9_v2:
mov rax, rdi
shrd rax, rsi, 32
mov rdx, rsi
mov r8d, eax
shr rdx, 32
mov eax, edi
add rax, rdx
lea rax, [rax+r8*4]
mov esi, esi
lea rcx, [rax+rsi*8]
sub rcx, rsi
mov rax, rcx
movabs rdx, -2049638230412172401
mul rdx
mov rax, rdx
shr rax, 3
and rdx, -8
add rdx, rax
mov rax, rcx
sub rax, rdx
ret
【问题讨论】:
-
@stark 我在
uint64_t上做%,而不是unsigned __int128。 -
我想有趣的部分在
__umodti3函数中。但无论如何,您的实现是专门为% 9编写的,而__umodti3是通用的% n。 -
__umodti3是一个通用的除法函数,所以它不能像% 9的优化版本一样快。至于为什么 GCC 或 Clang 都没有自动应用优化这一点,我们只能推测 - 很可能它只是不需要那么频繁并且不值得开发工作。值得注意的是,uint64_t % 9确实针对乘法和移位进行了优化。 -
解释很简单:编译器作者没有优化
__int128模。通常整数除法可以优化为乘法,乘法可以(通常)优化为移位和加法。尝试__int128部门向自己证明它没有优化。然后与__int64除法比较,你会发现区别。 -
@Jabberwocky:
mov esi,esi将rsi的最高 32 位设置为零(就像movzx rsi,esi一样)。
标签: c x86-64 compiler-optimization modular-arithmetic 128-bit