gpt4 book ai didi

optimization - 加快循环

转载 作者:行者123 更新时间:2023-11-29 07:56:40 24 4
gpt4 key购买 nike

我有以下代码:

for chunk in imagebuf.chunks_mut(4) {
let temp = chunk[0];
chunk[0] = chunk[2];
chunk[2] = temp;
}

对于 40000 个 u8 的数组,在我的机器上大约需要 2.5 毫秒,使用 cargo build --release 编译。

对于完全相同的数据,以下 C++ 代码大约需要 100 us(通过实现它并使用 FFI 从 rust 调用它来验证):

for(;imagebuf!=endbuf;imagebuf+=4) {
char c=imagebuf[0];
imagebuf[0]=imagebuf[2];
imagebuf[2]=c;
}

我认为应该可以加快 Rust 实现的执行速度,使其与 C++ 版本一样快。

Rust 程序是使用 cargo --release 构建的,C++ 程序是在没有任何优化标志的情况下构建的。

有什么提示吗?

最佳答案

我无法重现您得到的时间。您的测量方式可能有误(或者我有 😉)。在我的机器上,两个版本的运行时间完全相同。

在这个答案中,我将首先比较 C++ 和 Rust 版本的汇编输出。之后我将描述如何重现我的计时。


程序集比较

我使用令人惊叹的编译器资源管理器(Rust codeC++ Code)生成了汇编代码。我也编译了激活优化 (-O3) 的 C++ 代码,以使其成为一个公平的游戏(尽管 C++ 编译器优化对测量的时间没有影响)。这是生成的程序集(左边是 Rust,右边是 C++):

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 |

您可以立即看到 C++ 实际上产生的汇编少得多(没有优化 C++ 产生的指令几乎与 Rust 一样多)。我不确定 Rust 产生的所有附加指令,但至少有一半是用于边界检查的。但据我所知,这种绑定(bind)检查不是针对通过 [] 进行的实际访问,而是每次循环迭代一次。这仅适用于切片长度不能被 4 整除的情况。但我想 Rust 程序集可能会更好(即使有绑定(bind)检查)。

如评论中所述,您可以使用 get_unchecked() 删除绑定(bind)检查和 get_unchecked_mut() .但是请注意,这不会影响我的测量结果!

最后:你应该使用 [&]::swap(i, j)在这里。

for chunk in imagebuf.chunks_mut(4) {
chunk.swap(0, 2);
}

同样,这并没有显着影响性能。但它的代码更短更好。


测量

我使用了这个 C++ 代码(在 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;
}
}

我编译它:

gcc -c -O3 foocpp.cpp && ar rvs libfoocpp.a foocpp.o

然后我用这个 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);
}

无处不在的 black_box() 阻止编译器优化它不应该优化的地方。我用(夜间编译器)执行它:

LIBRARY_PATH=.:$LIBRARY_PATH cargo run --release

给我 (i7-6700HQ) 这样的值:

rust: Duration { secs: 0, nanos: 30583 }
cpp: Duration { secs: 0, nanos: 30810 }

时间波动很多(远远超过两个版本之间的差异)。不过,我不确定为什么 Rust 生成的额外程序集不会导致执行速度变慢。

关于optimization - 加快循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42338158/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com