使用单个 aarch64 指令获取余数?
Obtaining remainder using single aarch64 instruction?
我正在为 ARM8 (aarch64) 编写一些汇编代码。我想做一个除法并将获得的余数用于进一步计算。在 x86 中,当我使用
'div',我知道我的剩余部分保存在 RDX 中。我的问题是 - 是否有与 aarch64 指令集相同的指令?我知道 'udiv' 和 'sdiv' 做无符号和有符号除法并得到商。有没有一条指令可以给我余数? (我想要 c 中的 % 模运算符)。我知道我可以使用代数获得它,只是想确认我没有错过更简单的方法。
除了可以优化为 and
的恒定二次幂除数外,没有任何指令可以计算除法的余数。您可以,但是可以非常巧妙地分两次完成:
// input: x0=dividend, x1=divisor
udiv x2, x0, x1
msub x3, x2, x1, x0
// result: x2=quotient, x3=remainder
计算余数不是一条指令
Clang C 编译器生成了以下用于模计算的代码:
udiv x10, x0, x9
msub x10, x10, x9, x0
好消息,这并不慢!
虽然 x86 在一条指令中执行此操作,但这并没有使它更快。
在 Apple M-1 上,上述指令对的执行时间与单个步骤的执行时间大致相同。这可能是由于 that decodes multiple instructions into a single µ-op. It could also be due to parallelism in multiple execution units。可能,它是在一个 EU 中完成的,其中除法计算的余数被缓存并立即返回。
无论采用何种实现方式,它似乎都与英特尔的单指令形式一样快。
仅限部门
时间:
$ time ./a.out 12345678901
Total: 301123495054
real 0m10.036s
user 0m9.668s
sys 0m0.031s
生成的指令:
udiv x10, x0, x9
仅余数
时间:
$ time ./a.out 12345678901
Total: 8612082846779832640
real 0m10.190s
user 0m9.768s
sys 0m0.070s
生成的指令:
udiv x10, x0, x9
msub x10, x10, x9, x0
除法和余数
时间:
$ time ./a.out 12345678901
Total: 8612083123211969892
real 0m10.103s
user 0m9.752s
sys 0m0.019s
生成的指令:
udiv x10, x0, x9
msub x11, x10, x9, x0
基准代码
以下 C 代码可以是 运行,其中 q = n / d
或 r = n % d
被注释掉:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned long long n, d, q=1, r=1, total=0;
n = strtoull(argv[1], NULL, 10);
total = 0;
for (d=1 ; d<=n ; d++) {
q = n / d;
r = n % d;
total += q + r;
}
printf("Total: %llu", total);
return 0;
}
我正在为 ARM8 (aarch64) 编写一些汇编代码。我想做一个除法并将获得的余数用于进一步计算。在 x86 中,当我使用 'div',我知道我的剩余部分保存在 RDX 中。我的问题是 - 是否有与 aarch64 指令集相同的指令?我知道 'udiv' 和 'sdiv' 做无符号和有符号除法并得到商。有没有一条指令可以给我余数? (我想要 c 中的 % 模运算符)。我知道我可以使用代数获得它,只是想确认我没有错过更简单的方法。
除了可以优化为 and
的恒定二次幂除数外,没有任何指令可以计算除法的余数。您可以,但是可以非常巧妙地分两次完成:
// input: x0=dividend, x1=divisor
udiv x2, x0, x1
msub x3, x2, x1, x0
// result: x2=quotient, x3=remainder
计算余数不是一条指令
Clang C 编译器生成了以下用于模计算的代码:
udiv x10, x0, x9
msub x10, x10, x9, x0
好消息,这并不慢!
虽然 x86 在一条指令中执行此操作,但这并没有使它更快。
在 Apple M-1 上,上述指令对的执行时间与单个步骤的执行时间大致相同。这可能是由于
无论采用何种实现方式,它似乎都与英特尔的单指令形式一样快。
仅限部门
时间:
$ time ./a.out 12345678901
Total: 301123495054
real 0m10.036s
user 0m9.668s
sys 0m0.031s
生成的指令:
udiv x10, x0, x9
仅余数
时间:
$ time ./a.out 12345678901
Total: 8612082846779832640
real 0m10.190s
user 0m9.768s
sys 0m0.070s
生成的指令:
udiv x10, x0, x9
msub x10, x10, x9, x0
除法和余数
时间:
$ time ./a.out 12345678901
Total: 8612083123211969892
real 0m10.103s
user 0m9.752s
sys 0m0.019s
生成的指令:
udiv x10, x0, x9
msub x11, x10, x9, x0
基准代码
以下 C 代码可以是 运行,其中 q = n / d
或 r = n % d
被注释掉:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned long long n, d, q=1, r=1, total=0;
n = strtoull(argv[1], NULL, 10);
total = 0;
for (d=1 ; d<=n ; d++) {
q = n / d;
r = n % d;
total += q + r;
}
printf("Total: %llu", total);
return 0;
}