gpt4 book ai didi

loops - 删除 Rust 循环中的边界检查以尝试达到最佳编译器输出

转载 作者:行者123 更新时间:2023-12-03 11:25:56 25 4
gpt4 key购买 nike

为了确定我是否可以/应该使用 Rust 而不是默认的 C/C++,我正在研究各种边缘情况,主要是考虑到这个问题:在 0.1% 的情况下很重要,我总是能得到编译器输出和 gcc 一样好(带有适当的优化标志)?答案很可能是否定的,但让我们看看......
Reddit有一个相当特殊的例子研究无分支排序算法的子程序的编译器输出。
这是基准 C 代码:

#include <stdint.h>
#include <stdlib.h>
int32_t* foo(int32_t* elements, int32_t* buffer, int32_t pivot)
{
size_t buffer_index = 0;

for (size_t i = 0; i < 64; ++i) {
buffer[buffer_index] = (int32_t)i;
buffer_index += (size_t)(elements[i] < pivot);
}
}
这是 goldbolt link带有编译器输出。
使用 Rust 的第一次尝试看起来像这样:
pub fn foo0(elements: &Vec<i32>, mut buffer: [i32; 64], pivot: i32) -> () {
let mut buffer_index: usize = 0;
for i in 0..buffer.len() {
buffer[buffer_index] = i as i32;
buffer_index += (elements[i] < pivot) as usize;
}
}
有相当多的边界检查正在进行,参见 godbolt .
下一次尝试消除了第一次边界检查:
pub unsafe fn foo1(elements: &Vec<i32>, mut buffer: [i32; 64], pivot: i32) -> () {
let mut buffer_index: usize = 0;
for i in 0..buffer.len() {
unsafe {
buffer[buffer_index] = i as i32;
buffer_index += (elements.get_unchecked(i) < &pivot) as usize;
}
}
}
这样好一些(请参阅与上面相同的 Godbolt 链接)。
最后,让我们尝试完全删除边界检查:
use std::ptr;

pub unsafe fn foo2(elements: &Vec<i32>, mut buffer: [i32; 64], pivot: i32) -> () {
let mut buffer_index: usize = 0;
unsafe {
for i in 0..buffer.len() {
ptr::replace(&mut buffer[buffer_index], i as i32);
buffer_index += (elements.get_unchecked(i) < &pivot) as usize;
}
}
}
这产生与 foo1 相同的输出, 所以 ptr::replace仍然执行边界检查。我当然超出了我的深度,在这里,那些 unsafe操作。这导致了我的两个问题:
  • 如何消除边界检查?
  • 像这样分析边缘情况是否有意义?或者,如果呈现整个算法而不是其中的一小部分,Rust 编译器是否会看穿所有这些。

  • 关于最后一点,我很好奇,一般来说,Rust 是否可以像“文字”一样被屠杀,即接近金属,就像 C 一样。经验丰富的 Rust 程序员可能会对这方面的调查感到畏缩,但这里是......

    最佳答案

    • How can the bounds check be eliminated?

    数组,通过对切片的解引用强制,有一个 unchecked form of mutable get也。
    pub unsafe fn foo(elements: &Vec<i32>, mut buffer: [i32; 64], pivot: i32) {
    let mut buffer_index: usize = 0;
    for i in 0..buffer.len() {
    unsafe {
    *buffer.get_unchecked_mut(buffer_index) = i as i32;
    buffer_index += (elements.get_unchecked(i) < &pivot) as usize;
    }
    }
    }
    这可能会产生与使用 Clang 编译等效 C 代码获得的机器代码相同的机器代码。 https://godbolt.org/z/ddxP1P
    • Does it even make sense to analyze edge cases edge cases like this? Or would the Rust compiler see through all this if presented with the whole algorithm instead of only a small fraction thereof.

    与往常一样,即使您已经确定了该部分代码中的瓶颈,也可以对任何这些情况进行基准测试。否则,有朝一日可能会后悔,这是一种过早的优化。特别是在 Rust 中,决定写 unsafe代码不应掉以轻心。可以肯定地说,在许多情况下,仅消除边界检查的努力和风险就超过了预期的性能 yield 。

    Regarding the last point, I'm curious, in general, whether Rust can be butchered to the point where it is as "literal", i.e. close to the metal, as C is.


    不,您不希望这样做有两个主要原因:
  • 尽管 Rust 中的抽象具有强大的功能,但与 C++ 类似,不为不使用的东西付费的原则仍然非常相关。见 what makes an abstraction zero-cost .在边界检查的情况下,这仅仅是语言设计决策的结果,即当编译器无法确保此类访问是内存安全时始终执行空间检查。
  • C is not that low-level反正。它可能看起来像字面意思并且接近金属,直到它真的不是。

  • 也可以看看:
  • Does Rust's array bounds checking affect performance?
  • Why does my code run slower when I remove bounds checks?
  • 关于loops - 删除 Rust 循环中的边界检查以尝试达到最佳编译器输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65217177/

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