在 Rust 中迭代标量变量的有效方法

问题描述 投票:0回答:1

有时我有多个相同类型的标量变量,并且想要迭代它们。

为简单起见,我们假设类型为

usize
,变量数量为
1000
,这意味着它们的数组适合堆栈。

此后,我们将术语“快”定义为最坏情况的时间复杂度;如果一种方法“可能”非常快,那么我们并不是说它很快,因为编译器优化并不总是保证会发生。 方法1

这是一个仅供演示的人工示例(

playground

): use rand::prelude::*; fn main() { let mut rng = StdRng::seed_from_u64(0); loop { let a1 = rng.gen::<usize>(); let a2 = rng.gen::<usize>(); let a3 = rng.gen::<usize>(); /* ... */ let a1000 = rng.gen::<usize>(); for e in vec![a1, a2, a3, /* ... */ a1000] { println!("{}", e); } } }

在此示例中,我们有许多标量变量 
a1

a2
、...、
a1000
,为了迭代它们,我们将它们存储在
Vec<usize>
中。
显然这种方法效率低下,因为它分配堆内存并将所有变量复制到其中。

方法2

如果我们使用

[usize; 1000]

类型的数组,我认为数组本身是在堆栈中分配的。但是,我认为变量仍然被复制到其中。

for e in [a1, a2, a3, /* ... */ a1000] {
    println!("{}", e);
}

方法3

如果我们使用

[&usize; 1000]

类型的数组,现在变量不会被复制。然而,我认为这种方法在内部创建了许多

pointer
,我认为这与复制整数一样慢(将整数作为常量引用传递与复制 - Stack Overflow)。 for e in [&a1, &a2, &a3, /* ... */ &a1000] { println!("{}", e); }

我想最小化堆分配和复制。有什么好的办法吗?

我们有这种幼稚的方法,但可读性和可维护性太低(并且由于二进制大小的增加,其最坏情况的时间复杂度可能不好):

println!("{}", a1); println!("{}", a2); /* ... */ println!("{}", a1000);

编辑:

这是我目前最好的方法:

use rand::prelude::*; macro_rules! for_each { ($f:expr, $($var:ident),*) => { $( $f($var); )* }; } fn f(x: usize) { println!("{}", x); } fn main() { let mut rng = StdRng::seed_from_u64(0); { let a1 = rng.gen::<usize>(); let a2 = rng.gen::<usize>(); let a3 = rng.gen::<usize>(); /* ... */ let a1000 = rng.gen::<usize>(); for_each!(f, a1, a2, a3, /* ... */ a1000); } }


performance loops rust memory
1个回答
0
投票
for

循环确实会很慢,但并不是你想象的那样:它会很慢,因为 LLVM 无法优化整个数组

的副本来创建迭代器。元素本身将被优化以几乎总是就地生成。
这不能保证,但 codegen 的没有任何

是可以保证的。理论上,编译器可能会在一天早上醒来并决定完全停止优化代码,并且按照标准(无论如何 Rust 中不存在该标准)就没问题。

实际上,编译器不会这样做,就像他们不会这样做一样,我们知道

他们会执行一些优化。人们在性能关键型程序中始终依赖这种直觉。大多数情况下它都有效(但如果您不确定的话,检查代码生成器总是好的,并且始终进行基准测试)。

如果你想更确定,你可以直接在数组中创建元素(没有中间变量) - for e in [rng.gen(), rng.gen(), ...]

,但我可以告诉你这对于

usize

这样的类型并不重要。
要解决 for 循环的问题,您可以迭代切片:

let arr = [a1, a2, a3, /* ... */ a1000]; for &e in &arr { println!("{}", e); }


© www.soinside.com 2019 - 2024. All rights reserved.