代码太天真了:
use std::time;
fn main() {
const NUM_LOOP: u64 = std::u64::MAX;
let mut sum = 0u64;
let now = time::Instant::now();
for i in 0..NUM_LOOP {
sum += i;
}
let d = now.elapsed();
println!("{}", sum);
println!("loop: {}.{:09}s", d.as_secs(), d.subsec_nanos());
}产出如下:
$ ./test.rs.out
9223372036854775809
loop: 0.000000060s
$ ./test.rs.out
9223372036854775809
loop: 0.000000052s
$ ./test.rs.out
9223372036854775809
loop: 0.000000045s
$ ./test.rs.out
9223372036854775809
loop: 0.000000041s
$ ./test.rs.out
9223372036854775809
loop: 0.000000046s
$ ./test.rs.out
9223372036854775809
loop: 0.000000047s
$ ./test.rs.out
9223372036854775809
loop: 0.000000045s节目几乎马上就结束了。我还用for循环在C中编写了一个等价的代码,但它运行了很长时间。我想知道是什么让铁锈密码这么快。
C代码:
#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
double time_elapse(struct timespec start) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec - start.tv_sec +
(now.tv_nsec - start.tv_nsec) / 1000000000.;
}
int main() {
const uint64_t NUM_LOOP = 18446744073709551615u;
uint64_t sum = 0;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
for (int i = 0; i < NUM_LOOP; ++i) {
sum += i;
}
double t = time_elapse(now);
printf("value of sum is: %llu\n", sum);
printf("time elapse is: %lf sec\n", t);
return 0;
}Rust代码使用-O编译,C代码使用-O3编译。C代码运行得太慢了,还没有停止。
修复了visibleman和San深处发现的漏洞后,这两个程序几乎没有时间就打印出了相同的数字。我试图将NUM_LOOP减少一个,考虑到溢出,结果似乎是合理的。此外,使用NUM_LOOP = 1000000000,两个程序都不会溢出,并在短时间内产生正确的答案。这里使用什么优化?我知道我们可以使用像(0 + NUM_LOOP - 1) * NUM_LOOP / 2这样的简单方程来计算结果,但我不认为这种计算是由具有溢出情况的编译器完成的。
发布于 2018-10-24 12:31:34
您的锈蚀代码(没有打印和计时)编译为(论哥德波特):
movabs rax, -9223372036854775807
retLLVM只对整个函数进行折叠,并为您计算最终值。
让我们让上限动态(非常量)来避免这种侵略性的常数折叠:
pub fn foo(num: u64) -> u64 {
let mut sum = 0u64;
for i in 0..num {
sum += i;
}
sum
}这导致(哥德波特):
test rdi, rdi ; if num == 0
je .LBB0_1 ; jump to .LBB0_1
lea rax, [rdi - 1] ; sum = num - 1
lea rcx, [rdi - 2] ; rcx = num - 2
mul rcx ; sum = sum * rcx
shld rdx, rax, 63 ; rdx = sum / 2
lea rax, [rdx + rdi] ; sum = rdx + num
add rax, -1 ; sum -= 1
ret
.LBB0_1:
xor eax, eax ; sum = 0
ret如您所见,优化器理解从0到num的所有数字之和,并将循环替换为常数公式:((num - 1) * (num - 2)) / 2 + num - 1。对于上面的例子:优化器可能首先将代码优化成这个常量公式,然后进行常量折叠。
附加说明
clang 生成完全相同的程序集。 (不足为奇)。然而,GCC似乎不知道这种优化和生成您期望的程序集(循环)。。(0..num).sum()。尽管使用了更多的抽象层(即迭代器),但编译器生成的代码与上面的完全相同。Duration,可以使用{:?}格式说明符。println!("{:.2?}", d);以最合适的单位打印持续时间,精度为2。这是打印几乎所有基准的时间的好方法。发布于 2018-10-24 05:57:44
由于int永远不会像您的NUM_LOOP那么大,程序将永远循环。
const uint64_t NUM_LOOP = 18446744073709551615u;
for (int i = 0; i < NUM_LOOP; ++i) { // Change this to an uint64_t如果您修复了int,编译器将在这两种情况下优化这些循环。
发布于 2018-10-24 05:57:58
您的代码被困在一个无限循环中。
比较i < NUM_LOOP总是返回true,因为在到达NUM_LOOP之前,int i将环绕
https://stackoverflow.com/questions/52961867
复制相似问题