假设我有以下内容:
fn into_three_tuple(mut v: Vec<String>) -> (String, String, String) {
if v.len() == 3 {
// ???
} else {
panic!()
}
}
Run Code Online (Sandbox Code Playgroud)
我应该替换什么???才能获得最佳性能?
当然,我可以做
...
if v.len() == 3 {
let mut iter = v.into_iter();
(v.next().unwrap(), v.next().unwrap(), v.next().unwrap())
} else {
...
Run Code Online (Sandbox Code Playgroud)
或类似地
if v.len() == 3 {
let mut iter = v.into_iter();
let e2 = v.pop.unwrap();
let e1 = v.pop.unwrap();
let e0 = v.pop.unwrap();
(e0, e1, e2)
} else {
...
Run Code Online (Sandbox Code Playgroud)
这两个实现都使用unwrap,如果我理解正确的话,它会执行运行时检查。但既然我们有条件v.len() == 3,我们知道向量保证有 3 个元素,所以运行时检查是不必要的。
此外,该into_iter解决方案可能会引入创建迭代器的额外开销,并且该解决方案可能会引入减少的内部字段pop的额外开销,这看起来很愚蠢,因为在提取元素后会立即删除(所以我们不关心是否它的字段是准确的)。vlenvlen
是否有某种(可能unsafe)更有效的方法(例如,直接获取任意索引处元素的所有权的某种方法)?
或者也许编译器已经足够聪明,可以跳过这些无关的操作?
或者我只能忍受次优的表现?
如果您想知道为什么我痴迷于如此微小的微观优化,我正在编写一个对速度相当关键的应用程序,并且此函数将被调用大量次。
标准库中提供了一种操作,可以从向量中提取固定数量的项:将其转换为数组。
\nuse std::convert::TryInto; // needed only if not using 2021 edition / Rust 1.55 or earlier\npub fn into_three_tuple(v: Vec<String>) -> (String, String, String) {\n let three_array: [String; 3] = v.try_into().unwrap();\n let [a, b, c] = three_array;\n (a, b, c)\n}\nRun Code Online (Sandbox Code Playgroud)\n我不太熟悉 x86 程序集的阅读,但这确实可以编译为具有更少分支的更简单的代码。我通常认为,在大多数情况下,这是解包三元素向量的最快方法;如果它确实较慢,那么这将是标准库中的性能错误,应该报告并修复。
\n您还应该考虑[String; 3]在程序的其余部分中使用数组而不是元组。该类型编写起来更短,并且它们允许您使用数组和切片操作来作用于所有三个字符串。此外,元组没有保证的内存布局,而数组有(尽管实际上在这种情况下它们可能是相同的)。
将返回类型更改为数组使得函数 \xe2\x80\x94 可能对类型声明有用,但不包含任何有趣的代码:
\npub fn into_three_array(v: Vec<String>) -> [String; 3] {\n v.try_into().unwrap()\n}\nRun Code Online (Sandbox Code Playgroud)\n