平方数求和函数的编译器优化差异及性能原因问询
平方数求和函数的编译器优化与性能差异疑问
Rust实现及优化后的汇编
以下是Rust语言实现的平方数求和朴素版本:
pub fn sum_of_squares(n: i32) -> i32 { let mut sum = 0; for i in 1..n+1 { sum += i*i; } sum }
使用rustc 1.65.0并开启-O3优化后,生成的汇编代码如下:
lea ecx, [rdi + 1] xor eax, eax cmp ecx, 2 jl .LBB0_2 lea eax, [rdi - 1] lea ecx, [rdi - 2] imul rcx, rax lea eax, [rdi - 3] imul rax, rcx shr rax imul eax, eax, 1431655766 shr rcx lea ecx, [rcx + 4*rcx] add ecx, eax lea eax, [rcx + 4*rdi] add eax, -3 .LBB0_2: ret
我原本预期编译器会直接套用平方数求和公式,但实际并未如此,反而出现了一个完全无法理解的“魔法数”1431655766。
Clang与GCC的优化输出
接着我对比了Clang和GCC对同功能C++函数的优化结果:
GCC 12.2 -O3生成的汇编
test edi, edi jle .L8 lea eax, [rdi-1] cmp eax, 17 jbe .L9 mov edx, edi movdqa xmm3, XMMWORD PTR .LC0[rip] xor eax, eax pxor xmm1, xmm1 movdqa xmm4, XMMWORD PTR .LC1[rip] shr edx, 2 .L4: movdqa xmm0, xmm3 add eax, 1 paddd xmm3, xmm4 movdqa xmm2, xmm0 pmuludq xmm2, xmm0 psrlq xmm0, 32 pmuludq xmm0, xmm0 pshufd xmm2, xmm2, 8 pshufd xmm0, xmm0, 8 punpckldq xmm2, xmm0 paddd xmm1, xmm2 cmp eax, edx jne .L4 movdqa xmm0, xmm1 mov eax, edi psrldq xmm0, 8 and eax, -4 paddd xmm1, xmm0 add eax, 1 movdqa xmm0, xmm1 psrldq xmm0, 4 paddd xmm1, xmm0 movd edx, xmm1 test dil, 3 je .L1 .L7: mov ecx, eax imul ecx, eax add eax, 1 add edx, ecx cmp edi, eax jge .L7 .L1: mov eax, edx ret .L8: xor edx, edx mov eax, edx ret .L9: mov eax, 1 xor edx, edx jmp .L7 .LC0: .long 1 .long 2 .long 3 .long 4 .LC1: .long 4 .long 4 .long 4 .long 4
GCC同样没有使用平方数求和公式,而且我无法理解为什么要判断数值是否大于17,其生成的指令数量也远多于Clang和Rust。
Clang 15.0.0 -O3生成的汇编
test edi, edi jle .LBB0_1 lea eax, [rdi - 1] lea ecx, [rdi - 2] imul rcx, rax lea eax, [rdi - 3] imul rax, rcx shr rax imul eax, eax, 1431655766 shr rcx lea ecx, [rcx + 4*rcx] add ecx, eax lea eax, [rcx + 4*rdi] add eax, -3 ret .LBB0_1: xor eax, eax ret
我搞不懂Clang的优化逻辑,但可以确定的是,Rustc、Clang、GCC三者都没有采用n(n+1)(2n+1)/6这个标准的平方数求和公式。
性能测试结果
我在11代Intel Core i7-11800H @2.30GHz处理器上对三者进行了100次测试,取平均结果如下:
Rust: 0.2 microseconds Clang: 3 microseconds gcc: 5 microseconds
Rust实现的性能明显优于GCC和Clang,请问有人能解释这种性能差异的原因吗?
补充代码
对应的C++实现
int sum_of_squares(int n){ int sum = 0; for(int i = 1; i <= n; i++){ sum += i*i; } return sum; }
基准测试代码
Rust基准代码
use std::time::Instant; pub fn sum_of_squares(n: i32) -> i32 { let mut sum = 0; for i in 1..n+1 { sum += i*i; } sum } fn main() { let start = Instant::now(); let result = sum_of_squares(1000); let elapsed = start.elapsed(); println!("Result: {}", result); println!("Elapsed time: {:?}", elapsed); }
C++基准代码
#include <chrono> #include <iostream> int sum_of_squares(int n){ int sum = 0; for(int i = 1; i <= n; i++){ sum += i*i; } return sum; } int main() { auto start = std::chrono::high_resolution_clock::now(); int result = sum_of_squares(1000); auto end = std::chrono::high_resolution_clock::now(); std::cout << "Result: " << result << std::endl; std::cout << "Elapsed time: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << " microseconds" << std::endl; return 0; }
内容的提问来源于stack exchange,提问作者merovingian
相关产品推荐
相关产品推荐

