【问题标题】:How to improve code generator when using iterators in Rust?在 Rust 中使用迭代器时如何改进代码生成器?
【发布时间】:2022-11-13 13:16:23
【问题描述】:

以下two functions 生成非常不同的结果汇编语言:

pub struct X {
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
}

pub fn f(a: u8, x: X) -> u64 {
    [
        (0b000001, x.a),
        (0b000010, x.b),
        (0b000100, x.c),
        (0b001000, x.d),
        (0b010000, x.e),
        (0b100000, x.f),
    ]
    .into_iter()
    .find(|(bb, _)| (*bb & a) != 0)
    .map_or(0, |(_, m)| m)
}

pub fn g(a: u8, x: X) -> u64 {
    match a {
        _ if (a & 0b000001) != 0 => x.a,
        _ if (a & 0b000010) != 0 => x.b,
        _ if (a & 0b000100) != 0 => x.c,
        _ if (a & 0b001000) != 0 => x.d,
        _ if (a & 0b010000) != 0 => x.e,
        _ if (a & 0b100000) != 0 => x.f,
        _ => 0,
    }
}

他们做同样的事情:基于位模式,返回正确的值。我更喜欢f,因为它将数据和逻辑分开,但会导致组装质量差。因为我正在运行模拟,所以一点就是很多。 (参见上面的操场链接的程序集,生成发布 asm)

f 中,Rust 不必要地在内存中构建数组,而不是识别出这些值已被使用并立即丢弃。 g 将数据和逻辑混合在一起,但 Rust 只是简单地进行比较然后返回结果,正如您所期望的那样。

有什么我可以做的来帮助这个迭代器风格的代码生成更好的代码,还是我最好写命令式风格?

【问题讨论】:

  • 不是对您问题的直接回答,但看起来您可以在此处使用leading_zeros()
  • @DanGetz - 哈哈,是的,在这种情况下。不幸的是,我有更复杂的评估。不确定 ctz 无论如何会如何简化这一点,因为我只是在比较位。
  • 有趣的是,如果您不自己预加载值而是使用引用,它们会生成几乎相同的程序集:playground。也许更容易预先优化引用的固定偏移量,而不是尝试回溯原始值的来源以省略数组。
  • 此外,您可能会从通过引用而不是通过值传递X 获得一些性能优势,因为这会减少寄存器压力/堆栈移动,而且我怀疑如果它已经在缓存中,间接会花费任何代价。但是,当然,测量!
  • “……还是我写命令式风格更好?”- 我个人认为无论如何这里的匹配版本比迭代版本要清晰得多。

标签: rust iterator


【解决方案1】:

还有稍微短一点的

pub fn h(a: u8, x: X) -> u64 {
    match a.trailing_zeros() {
        0 => x.a,
        1 => x.b,
        2 => x.c,
        3 => x.d,
        4 => x.e,
        5 => x.f,
        _ => 0
    }
}

不知道它的组件看起来如何。

【讨论】:

    猜你喜欢
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-07
    • 1970-01-01
    • 2018-05-24
    相关资源
    最近更新 更多