我是Rust的新手,最近我一直在搞乱它。我很好奇在Rust中使用索引访问数组的性能与C相比。
我做了这两个程序:
fn main() {
let mut arr: [[i32; 1000]; 1000] = [[0; 1000]; 1000];
for t in 0..1000 {
for i in 0..1000 {
for j in 0..1000 {
arr[i][j] = (i * j) as i32;
}
}
}
}
并在C:
#include <stdlib.h>
#include <string.h>
#define ARRSIZE 1000
int main() {
int ** arr = malloc(sizeof(int*) * ARRSIZE);
int i, j, t;
for (i = 0; i < ARRSIZE; ++i) {
arr[i] = malloc(sizeof(int) * ARRSIZE);
memset((void*) arr[i], 0, sizeof(int) * ARRSIZE);
}
for (t = 0; t < ARRSIZE; ++t) {
for (i = 0; i < ARRSIZE; ++i) {
for (j = 0; j < ARRSIZE; ++j) {
arr[i][j] = i * j;
}
}
}
for (i = 0; i < ARRSIZE; ++i) {
free(arr[i]);
}
free(arr);
}
我们的想法是创建一个1000x1000的2D数组,并在每次迭代中迭代每个元素1000次,进行简单的算术运算。
两者之间的性能差距很大(C版本需要大约3秒,Rust版本需要45秒)。这是正常的,还是我在Rust版本中做错了什么?
编辑:我尝试禁用边界检查并获得相同的结果。
感谢。
答案 0 :(得分:9)
首先,我假设您已经编译了而没有优化,因为我无法重现您所描述的时间而无需在调试模式下编译,具体而言没有积极优化。在这种情况下,鉴于Rust正在做更多工作和已知在调试模式下生成次优代码,因此差异并不令人惊讶。
其次,这两个计划并不相同。 C代码分配1001堆数组,Rust代码没有分配任何。因此,只要您执行开关优化,Rust代码就会比C代码更快运行。
所以现在我们需要将C程序修改为 not allocate。鉴于此:
#define ARRSIZE 1000
int main() {
int arr[ARRSIZE][ARRSIZE] = { { 0 } };
int i, j, t;
for (t = 0; t < ARRSIZE; ++t) {
for (i = 0; i < ARRSIZE; ++i) {
for (j = 0; j < ARRSIZE; ++j) {
arr[i][j] = i * j;
}
}
}
}
我使用gcc -O
(使用GCC 4.8.4)和rustc -O
(使用Rust 1.7.0)编译的结果是:
$ time ./c-2; time ./rs-1
real 0m0.335s
user 0m0.328s
sys 0m0.000s
real 0m0.002s
user 0m0.000s
sys 0m0.000s
这是如此短暂,毫无意义。 但它变得更糟。 Rust程序如此之快的原因是程序非常简单,LLVM 完全删除它。该程序没有可见的副作用,因此它只是编译成一个立即退出的空二进制文件。
没有有意义的可以从这个基准测试中收集,除了Rust生成慢速调试可执行文件(已经相当熟知)。
答案 1 :(得分:8)
要以(或多或少)有意义的方式(和其他人一起玩)来测试,我修改了这样的程序:
<强>锈强>:
#![feature(test)]
extern crate test;
fn main() {
let mut arr: [[i32; 1000]; 1000] = [[0; 1000]; 1000];
for _ in 0..1000 {
for i in 0..1000 {
for j in 0..1000 {
arr[i][j] = (i * j) as i32;
}
}
}
test::black_box(arr);
}
C (使用堆栈,代码主要由DK提供。):
#define ARRSIZE 1000
int main() {
int arr[ARRSIZE][ARRSIZE] = { { 0 } };
int i, j, t;
for (t = 0; t < ARRSIZE; ++t) {
for (i = 0; i < ARRSIZE; ++i) {
for (j = 0; j < ARRSIZE; ++j) {
arr[i][j] = i * j;
}
}
}
asm ("" : : "r" (arr));
}
black_box
和asm(...)
用于阻止优化程序删除所有代码。但是,我使用clang
代替gcc
来完成此工作。所以相比之下:
$ rustc -O test.rs | $ clang -O2 test.c
$ time ./test | $ time ./a.out
|
real 0m0.537s | real 0m0.546s
user 0m0.532s | user 0m0.544s
sys 0m0.004s | sys 0m0.004s
同一程序的单次执行之间的执行时间比这两个程序在运行时的差异更大。
我想说的是什么(以及DK已经说过的话):差别应该是微不足道的。两者都应该做同样的工作;只有Rust也会绑定检查。但在这种情况下,LLVM优化器可能会删除这些。只记得建立在发布模式;)