将性能关键循环从C转换为Rust

Mar*_*kus 1 c translate rust

我正在尝试将一些旧的C代码重写为Rust - 我是新手.我遇到的一个反复出现的问题是C代码有很多这样的循环:

for (i = startIndex; i < asize; i++)   
{
    if (firstEdge < 0 && condLeft(i))
    {
        firstEdge = i;
    }

    RightIndex = asize-1-i;
    if (firstEdgeRight < 0 && condRight(RightIndex))
    {
        firstEdgeRight = RightIndex;
    }

    // Found both edges
    if (firstEdge >= 0 && firstEdgeRight >= 0) {
        break;  
    }
}
Run Code Online (Sandbox Code Playgroud)

你会如何以高效的方式将其转化为Rust?我的问题是,虽然我可能得到我想要的功能,但我不确定如何获得(大致)相同的速度.

这部分代码是我们代码中的主要瓶颈(至少这部分代码),并且在翻译时它希望保留以下属性.

  1. 循环应该尽快破坏,因为asize它可能非常大.
  2. 双方firstEdgefirstEdgeRight在同一时间大致找到.因此,只有一个循环而不是两个循环是一件好事 - 为了避免再次从头开始搜索(即使我认为这个解决方案会杀死预取器(但我不确定,也许旧机器运行代码)甚至没有预取器)).

虽然性能很重要,但可读性当然更为重要:)

编辑好的,这是我可能实现的Rust(cond_right()并且cond_left()被遗漏了).我想到的是:

  1. 这是否是其他人如果必须从头开始实施的话呢?
  2. 我真的需要制作first_edgefirst_edge_right变异吗?它们在我的实现中,但我感觉不对,因为它们只被分配一次.
let mut first_edge = -1;
let mut first_edge_right = -1;

// Find first edge

let start_index = 300; // or something
let asize = 10000000;

for i in start_index..asize {
    if first_edge < 0 && cond_left(i) {
        first_edge = i;
    }

    let right_index = asize - i -1;
    if first_edge_right < 0 && cond_right(right_index) {
        first_edge_right = right_index;
    }

    if (first_edge >= 0 && first_edge_right >= 0) {
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

你需要做好选择的准备:

  1. 你会如何以高效的方式将其转化为Rust?

  2. 虽然性能很重要,但可读性当然更为重要

哪个对你来说更重要?这是我编写代码的方式,假设我已经理解了你的要求:

fn left_condition(i: usize) -> bool {
    i > 1000
}

fn right_condition(i: usize) -> bool {
    i % 73 == 0
}

fn main() {
    let start_index = 300;
    let asize = 10000000;

    let left = (start_index..asize).position(left_condition);
    let right = (start_index..asize).rev().position(right_condition);

    println!("{:?}, {:?}", left, right);
}
Run Code Online (Sandbox Code Playgroud)

我们从左到右迭代一次,从右到左迭代一次.我的直觉告诉我,这将为代码提供简单的分支预测,以线性方式访问内存,这两者都应该是可优化的.

但是,变量名称asize让我暂停.它当然听起来像"数组大小"的缩写.如果是这种情况,那么我会100%建议使用切片而不是数组索引.为什么?因为数组access(foo[0])通常有边界检查的开销.我会用切片写一些东西:

let data = vec![0; 10_000_000];
let start_index = 300;

let slice = &data[start_index..];

let left = slice.iter().position(|&i| left_condition(i));
let right = slice.iter().rev().position(|&i| right_condition(i));
Run Code Online (Sandbox Code Playgroud)

但是,对于您的问题,只有一个可能的真实答案:

使用分析器

使用分析器

使用分析器

使用分析器

只知道您的实际数据,条件的实际实现,您正在运行的其余代码等,您才能真正了解某些事情的速度.

因此,只有一个循环而不是两个循环是一件好事 - 为了避免再次从头开始搜索

这对我来说不直观,所以我希望看到支持声明的分析结果.