Ива*_*ван 4 arrays monads vector slice rust
我想为 slice 中的每个元素调用一个函数[0+k .. n]
,其中k
是偏移量,n
是向量中的元素数。重要的是,我想要原始切片中元素的索引。
我找到了两种方法来做到这一点:
使用enumerate
和skip
开始项目
vec.iter().enumerate().skip(k).map(|(i, v)| (f(v), i)).min()
Run Code Online (Sandbox Code Playgroud)取一个子切片并将偏移量添加到`enumerate 的索引中
vec[k..].iter().enumerate().map(|(i, v)| (f(v), i + k)).min()
Run Code Online (Sandbox Code Playgroud)在这两种情况下,vec
都是字符串向量,并f
返回字符串中的特定字符 ( v.chars().nth(offset)
)。这些解决方案中哪个最有效?
让我们以这段代码为例。它与您的示例类似,但更简单:
fn main() {
let items = ["a", "bb", "ccc", "dddd", "eeeee"];
let k = 3;
let one = items.iter().enumerate().skip(k).map(|(i, v)| (v.len(), i));
let two = items[k..].iter().enumerate().map(|(i, v)| (v.len(), i + k));
// Sanity check that the results are the same
let items1: Vec<_> = one.collect();
let items2: Vec<_> = two.collect();
println!("{}", items1 == items2);
}
Run Code Online (Sandbox Code Playgroud)
哪个性能更好是一个棘手的话题。Rust 和 LLVM 有很好的优化,可以使代码非常快。
纯粹根据我的直觉,如果我知道我将跳过“少数”项目,我可能会使用第一个,如果我不知道有多少或很多,我可能会使用第二个.
在第一种情况下,从概念上讲,您必须遍历要跳过的所有项目。优化器可能会减少这种情况,但是与enumerate
and的交互很复杂map
,所以我不会在不检查程序集的情况下指望它。
第二个 ( items[k..]
) 使用细分,这将是一个 O(1) 操作,因为它只是要索引到一块内存中。然后你做加法,这也很简单。
但是,唯一真正的测试是进行一些分析。我们将创建一个大型输入数组并开始:
fn main() {
let items = ["a", "bb", "ccc", "dddd", "eeeee"];
let items: Vec<_> = items.iter().cycle().take(10_000_000).collect();
let k = 371_223;
// let one = items.iter().enumerate().skip(k).map(|(i, v)| (v.len(), i));
let two = items[k..].iter().enumerate().map(|(i, v)| (v.len(), i + k));
// let items1: Vec<_> = one.collect();
let items2: Vec<_> = two.collect();
// println!("{}", items1.len());
println!("{}", items2.len());
}
Run Code Online (Sandbox Code Playgroud)
运行经过优化编译的代码,运行 10 次后的平均时间如下:
因此,与我的直觉所说的相反,第一个版本更快。我的基准测试完全有可能不正确,但这就是您应该对真实代码进行基准测试的原因。
另外,请注意,无论哪种方式,这都非常快。每个项目大约需要 15 或 16纳秒。朋友之间的一纳秒是多少?
归档时间: |
|
查看次数: |
992 次 |
最近记录: |