Rust优化循环?

Jan*_*ček 4 optimization gcc rust

我正在做一些非常简单的基准来比较C和Rust的性能.我使用了一个函数添加整数1 + 2 + ... + n(我可以通过手工计算验证的东西),其中n = 10^10.

Rust中的代码如下所示:

fn main() {
  let limit: u64 = 10000000000;
  let mut buf: u64 = 0;
  for u64::range(1, limit) |i| {
    buf = buf + i;
  }
  io::println(buf.to_str());
}
Run Code Online (Sandbox Code Playgroud)

C代码如下:

#include <stdio.h>
int main()
{
  unsigned long long buf = 0;
  for(unsigned long long i = 0; i < 10000000000; ++i) {
    buf = buf + i;
  }
  printf("%llu\n", buf);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我编译并运行它们:

$ rustc sum.rs -o sum_rust
$ time ./sum_rust
13106511847580896768

real        6m43.122s
user        6m42.597s
sys 0m0.076s
$ gcc -Wall -std=c99 sum.c -o sum_c
$ time ./sum_c
13106511847580896768

real        1m3.296s
user        1m3.172s
sys         0m0.024s
Run Code Online (Sandbox Code Playgroud)

然后我尝试使用优化标志,再次使用C和Rust:

$ rustc sum.rs -o sum_rust -O
$ time ./sum_rust
13106511847580896768

real        0m0.018s
user        0m0.004s
sys         0m0.012s
$ gcc -Wall -std=c99 sum.c -o sum_c -O9
$ time ./sum_c
13106511847580896768

real        0m16.779s
user        0m16.725s
sys         0m0.008s
Run Code Online (Sandbox Code Playgroud)

这些结果让我很惊讶.我确实期望优化会产生一些影响,但优化的Rust版本要快100000倍 :).

我尝试改变n(唯一的限制是u64,运行时间几乎为零),甚至尝试了一个不同的问题(1^5 + 2^5 + 3^5 + ... + n^5),结果相似:编译的可执行文件rustc -O比没有标志的情况快几个数量级,并且也是很多次比编译的同一算法更快gcc -O9.

所以我的问题是:发生了什么?:)我可以理解编译器优化1 + 2 + .. + n = (n*n + n)/2,但我无法想象任何编译器都可以派生出一个公式1^5 + 2^5 + 3^5 + .. + n^5.另一方面,据我所知,结果必须以某种方式计算(并且它似乎是正确的).

哦,并且:

$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
$ rustc --version
rustc 0.6 (dba9337 2013-05-10 05:52:48 -0700)
host: i686-unknown-linux-gnu
Run Code Online (Sandbox Code Playgroud)

huo*_*uon 8

是的,编译器确实使用1 + ... + n = n*(n+1)/2优化来移除循环,并且对于求和变量的任何幂都有类似的技巧.例如ķ 1是三角形数,ķ 2是锥体号码,ķ 3被取平方三角形号码等.通常,存在甚至一个公式来计算Σ ķ ķ p为任何p.


您可以使用更复杂的表达式,以便编译器没有任何删除循环的技巧.例如

fn main() {
  let limit: u64 = 1000000000;
  let mut buf: u64 = 0;
  for u64::range(1, limit) |i| {
    buf += i + i ^ (i*i);
  }
  io::println(buf.to_str());
}
Run Code Online (Sandbox Code Playgroud)

#include <stdio.h>
int main()
{
  unsigned long long buf = 0;
  for(unsigned long long i = 0; i < 1000000000; ++i) {
    buf += i + i ^ (i * i);
  }
  printf("%llu\n", buf);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这给了我

real    0m0.700s
user    0m0.692s
sys     0m0.004s
Run Code Online (Sandbox Code Playgroud)

real    0m0.698s
user    0m0.692s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)

分别(-O对于两个编译器).

  • 谢谢,我低估了编译器:)。但为什么 LLVM 包含如此特殊的优化呢? (2认同)