我有一个非常简单的程序,它从命令行获取整数参数并执行以下任务:
use clap::Parser ;
#[derive(Parser,Default)]
struct Args {
#[arg(short)]
number: u128
}
fn sum(n: u128) -> u128 {
let mut result: u128 = 0;
for i in 1..n { result += n/i; }
result
}
fn main() {
let args = Args::parse() ;
println!("{}", sum(args.number)) ;
}
Run Code Online (Sandbox Code Playgroud)
相当简单,对吧?好吧,当对“大”数字执行它时,例如 999999999,在我的机器上几乎需要十秒钟才能得到结果。
$ time ./target/release/main -n 999999999
20877697533
real 0m9.442s
user 0m9.370s
sys 0m0.030s
Run Code Online (Sandbox Code Playgroud)
但是,如果我禁止使用任何拍手并对值进行硬编码
$ time ./target/release/main -n 999999999
20877697533
real 0m9.442s
user 0m9.370s
sys 0m0.030s
Run Code Online (Sandbox Code Playgroud)
执行时间下降到两秒左右
$ time ./target/release/main
20877697533
real 0m2.398s
user 0m2.236s
sys 0m0.013s
Run Code Online (Sandbox Code Playgroud)
那么,是什么让 clap 版本这么慢呢?
999999999 适合 32 位整数。通过硬编码值,编译器会注意到这一点并将所有操作降级为 32 位操作,这比没有本机编译器支持的 128 位例程执行速度要快得多。
比较非硬编码循环的程序集(但删除了 clap,如 Schwern 的版本):
xorl %esi, %esi
cmpq $2, %r15
movq %rdx, %rax
sbbq $0, %rax
movl $0, %r13d
jb .LBB5_9
movq %r15, %rsi
addq $-1, %rsi
movq %rdx, %rdi
adcq $-1, %rdi
movq %r15, %rax
addq $-2, %rax
movq %rdx, %rcx
adcq $-1, %rcx
movq %rsi, 32(%rsp)
andl $3, %esi
movq %rsi, 40(%rsp)
cmpq $3, %rax
sbbq $0, %rcx
movq %rdx, 8(%rsp)
jae .LBB5_4
xorl %ebp, %ebp
movl $1, %edx
xorl %r13d, %r13d
xorl %ecx, %ecx
jmp .LBB5_6
.LBB5_4:
andq $-4, 32(%rsp)
movl $1, %edx
xorl %ebp, %ebp
xorl %r13d, %r13d
xorl %ecx, %ecx
xorl %esi, %esi
xorl %ebx, %ebx
movq %rdi, 64(%rsp)
.LBB5_5:
movq %rbx, 88(%rsp)
movq %rsi, 96(%rsp)
movq %rcx, 16(%rsp)
movq %rdx, (%rsp)
addq $1, %rdx
movq %rdx, 24(%rsp)
movq %rcx, %rbx
adcq $0, %rbx
movq %r15, %rdi
movq 8(%rsp), %rsi
movq (%rsp), %rdx
movq 16(%rsp), %rcx
movq __udivti3@GOTPCREL(%rip), %r14
callq *%r14
movq %r14, %r8
movq %rax, %r14
movq %rdx, %r12
addq %rbp, %r14
adcq %r13, %r12
movq (%rsp), %rax
addq $2, %rax
movq %rax, 80(%rsp)
movq 16(%rsp), %rax
adcq $0, %rax
movq %rax, 72(%rsp)
movq %r15, %rdi
movq 8(%rsp), %rsi
movq 24(%rsp), %rdx
movq %rbx, %rcx
movq %r8, %rbx
callq *%r8
movq %rbx, %r8
movq %rax, %rbx
movq %r15, %r13
movq %rdx, %rbp
addq %r14, %rbx
adcq %r12, %rbp
movq (%rsp), %rax
addq $3, %rax
movq %rax, 24(%rsp)
movq 16(%rsp), %r15
adcq $0, %r15
movq %r13, %rdi
movq 8(%rsp), %rsi
movq 80(%rsp), %rdx
movq 72(%rsp), %rcx
movq %r8, %r14
callq *%r8
movq %r14, %r8
movq %rax, %r12
movq %rdx, %r14
addq %rbx, %r12
adcq %rbp, %r14
addq $4, (%rsp)
adcq $0, 16(%rsp)
movq %r13, %rdi
movq 8(%rsp), %rsi
movq 24(%rsp), %rdx
movq %r15, %rcx
movq %r13, %r15
callq *%r8
movq 88(%rsp), %rbx
movq 96(%rsp), %rsi
movq 64(%rsp), %rdi
movq %rax, %rbp
movq %rdx, %r13
movq (%rsp), %rdx
addq %r12, %rbp
adcq %r14, %r13
addq $4, %rsi
adcq $0, %rbx
movq %rsi, %rax
xorq 32(%rsp), %rax
movq %rbx, %rcx
xorq %rdi, %rcx
orq %rax, %rcx
movq 16(%rsp), %rcx
jne .LBB5_5
.LBB5_6:
cmpq $0, 40(%rsp)
movq %rbp, %rsi
je .LBB5_9
xorl %r12d, %r12d
xorl %ebp, %ebp
movq %rdx, %rbx
movq %rcx, %r14
.LBB5_8:
movq %rsi, (%rsp)
addq $1, %rbx
adcq $0, %r14
movq %r15, %rdi
movq 8(%rsp), %rsi
callq *__udivti3@GOTPCREL(%rip)
movq (%rsp), %rsi
addq %rax, %rsi
adcq %rdx, %r13
addq $1, %r12
adcq $0, %rbp
movq %r12, %rax
xorq 40(%rsp), %rax
orq %rbp, %rax
movq %rbx, %rdx
movq %r14, %rcx
jne .LBB5_8
Run Code Online (Sandbox Code Playgroud)
到硬编码循环:
.LBB5_1:
movl $999999999, %eax
xorl %edx, %edx
divl %r8d
movl %eax, %r9d
addq %rcx, %r9
adcq $0, %rdi
addq $2, %r10
adcq $0, %r11
leal 1(%r8), %ecx
movl $999999999, %eax
xorl %edx, %edx
divl %ecx
movl %eax, %ecx
addq %r9, %rcx
adcq $0, %rdi
cmpq $999999997, %r8
sbbq $0, %rsi
movq %r10, %r8
movq %r11, %rsi
jb .LBB5_1
subq $88, %rsp
movq %rcx, 24(%rsp)
movq %rdi, 32(%rsp)
leaq 24(%rsp), %rax
movq %rax, 8(%rsp)
movq core::fmt::num::<impl core::fmt::Display for u128>::fmt@GOTPCREL(%rip), %rax
movq %rax, 16(%rsp)
leaq .L__unnamed_2(%rip), %rax
movq %rax, 56(%rsp)
movq $2, 64(%rsp)
movq $0, 40(%rsp)
leaq 8(%rsp), %rax
movq %rax, 72(%rsp)
movq $1, 80(%rsp)
leaq 40(%rsp), %rdi
callq *std::io::stdio::_print@GOTPCREL(%rip)
addq $88, %rsp
retq
Run Code Online (Sandbox Code Playgroud)
请特别注意此版本中完全没有对 的调用__udivti3。
| 归档时间: |
|
| 查看次数: |
93 次 |
| 最近记录: |