我有以下代码:
for chunk in imagebuf.chunks_mut(4) {
let temp = chunk[0];
chunk[0] = chunk[2];
chunk[2] = temp;
}
Run Code Online (Sandbox Code Playgroud)
对于40000 u8s 的数组,我的机器需要大约2.5 ms,使用编译cargo build --release.
以下C++代码对于完全相同的数据大约需要100 us(通过实现它并使用FFI从rust调用它来验证):
for(;imagebuf!=endbuf;imagebuf+=4) {
char c=imagebuf[0];
imagebuf[0]=imagebuf[2];
imagebuf[2]=c;
}
Run Code Online (Sandbox Code Playgroud)
我认为应该可以加速Rust实现,以便像C++版本一样快.
Rust程序是使用构建的cargo --release,C++程序是在没有任何优化标志的情况下构建的.
任何提示?
我无法重现你得到的时间.您测量(或我有)的方式可能有误.在我的机器上,两个版本都在同一时间运行.
在这个答案中,我将首先比较C++和Rust版本的汇编输出.之后我将描述如何重现我的时间.
我使用了令人惊奇的Compiler Explorer(Rust代码,C++代码)生成了汇编代码.我使用优化激活(-O3)编译了C++代码,以使其成为公平的游戏(但C++编译器优化对测量的时序没有影响).这是生成的程序集(Rust left,C++ right):
example::foo_rust: | foo_cpp(char*, char*):
test rsi, rsi | cmp rdi, rsi
je .LBB0_5 | je .L3
mov r8d, 4 |
.LBB0_2: | .L5:
cmp rsi, 4 |
mov rdx, rsi |
cmova rdx, r8 |
test rdi, rdi |
je .LBB0_5 |
cmp rdx, 3 |
jb .LBB0_6 |
movzx ecx, byte ptr [rdi] | movzx edx, BYTE PTR [rdi]
movzx eax, byte ptr [rdi + 2] | movzx eax, BYTE PTR [rdi+2]
| add rdi, 4
mov byte ptr [rdi], al | mov BYTE PTR [rdi-2], al
mov byte ptr [rdi + 2], cl | mov BYTE PTR [rdi-4], dl
lea rdi, [rdi + rdx] |
sub rsi, rdx | cmp rsi, rdi
jne .LBB0_2 | jne .L5
.LBB0_5: | .L3:
| xor eax, eax
ret | ret
.LBB0_6: |
push rbp +-----------------+
mov rbp, rsp |
lea rdi, [rip + panic_bounds_check_loc.3] |
mov esi, 2 |
call core::panicking::panic_bounds_check@PLT |
Run Code Online (Sandbox Code Playgroud)
您可以立即看到C++确实产生了更少的汇编(没有优化C++生成的指令几乎和Rust一样多).我不确定Rust生成的所有附加指令,但至少有一半是用于绑定检查.但据我所知,这种绑定检查不是通过实际访问,[]而是每次循环迭代.这只是因为切片的长度不能被4整除.但我想Rust程序集可能会更好(即使是绑定检查).
如评论中所述,您可以使用get_unchecked()和删除绑定检查get_unchecked_mut().但请注意,这不会影响我测量的性能!
最后:你应该[&]::swap(i, j)在这里使用.
for chunk in imagebuf.chunks_mut(4) {
chunk.swap(0, 2);
}
Run Code Online (Sandbox Code Playgroud)
这再次没有显着影响表现.但它是更短更好的代码.
我用过这个C++代码(in foocpp.cpp):
extern "C" void foo_cpp(char *imagebuf, char *endbuf);
void foo_cpp(char* imagebuf, char* endbuf) {
for(;imagebuf!=endbuf;imagebuf+=4) {
char c=imagebuf[0];
imagebuf[0]=imagebuf[2];
imagebuf[2]=c;
}
}
Run Code Online (Sandbox Code Playgroud)
我编译它:
gcc -c -O3 foocpp.cpp && ar rvs libfoocpp.a foocpp.o
Run Code Online (Sandbox Code Playgroud)
然后我用这个Rust代码测量一切:
#![feature(test)]
extern crate libc;
extern crate test;
use test::black_box;
use std::time::Instant;
#[link(name = "foocpp")]
extern {
fn foo_cpp(start: *mut libc::c_char, end: *const libc::c_char);
}
pub fn foo_rust(imagebuf: &mut [u8]) {
for chunk in imagebuf.chunks_mut(4) {
let temp = chunk[0];
chunk[0] = chunk[2];
chunk[2] = temp;
}
}
fn main() {
let mut buf = [0u8; 40_000];
let before = Instant::now();
foo_rust(black_box(&mut buf));
black_box(buf);
println!("rust: {:?}", Instant::now() - before);
// ----------------------------------
let mut buf = [0u8 as libc::c_char; 40_000];
let before = Instant::now();
let ptr = buf.as_mut_ptr();
let end = unsafe { ptr.offset(buf.len() as isize) };
unsafe { foo_cpp(black_box(ptr), black_box(end)); }
black_box(buf);
println!("cpp: {:?}", Instant::now() - before);
}
Run Code Online (Sandbox Code Playgroud)
在black_box()从优化它不应该到处都是阻止编译器.我用(夜间编译器)执行它:
LIBRARY_PATH=.:$LIBRARY_PATH cargo run --release
Run Code Online (Sandbox Code Playgroud)
给我(i7-6700HQ)这样的值:
rust: Duration { secs: 0, nanos: 30583 }
cpp: Duration { secs: 0, nanos: 30810 }
Run Code Online (Sandbox Code Playgroud)
时间波动很大(方式多于两个版本之间的差异).我不确定为什么Rust生成的附加程序集不会导致执行速度变慢.
| 归档时间: |
|
| 查看次数: |
105 次 |
| 最近记录: |