I want to call a function for each element in a slice [0+k .. n]
, where k
is an offset and n
is the number of elements in the vector. Importantly, I want the index of the element from the original slice.
我想为片中的每个元素调用一个函数[0+k]。n],其中k为偏移量,n为向量中元素的个数。重要的是,我希望元素的索引来自原始的片。
I have found two ways of doing this:
我发现了两种方法:
-
Use
enumerate
andskip
the beginning items使用枚举和跳过开始项。
vec.iter().enumerate().skip(k).map(|(i, v)| (f(v), i)).min()
-
Take a subslice and add the offset to the index from `enumerate
取一个子切片,将偏移量添加到“enumerate”索引中
vec[k..].iter().enumerate().map(|(i, v)| (f(v), i + k)).min()
In both cases, vec
is a vector of strings, and f
returns a specific character in the string (v.chars().nth(offset)
). Which of these solutions is most efficient?
在这两种情况下,vec都是字符串的向量,f返回字符串中的特定字符(v.chars().nth(偏移量))。哪种解决方案最有效?
1 个解决方案
#1
4
Let's use this code as an example. It's similar to your example, but a bit simpler:
让我们以这个代码为例。它类似于你的例子,但更简单一点:
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);
}
Which will be more performant is a tricky topic. Rust and LLVM have good optimizations that can make code quite fast.
这将是一个棘手的话题。Rust和LLVM有很好的优化,可以使代码非常快速。
Based purely on my gut feeling, I would probably use the first one if I knew that I was going to skip just "a few" of the items, and the second one if I didn't know how many or if it were a lot.
纯粹基于我的直觉,如果我知道我将跳过“一些”的条目,如果我不知道有多少,如果我不知道它有多少,我可能会使用第一个。
In the first case, you conceptually have to iterate through all of the items that you want to skip over. It is possible that the optimizer could reduce this down, but it's complicated by the interaction with enumerate
and map
, so I wouldn't count on it without inspecting assembly.
在第一种情况下,您必须在概念上遍历您想要跳过的所有项。优化器可能会减少这一点,但由于与enumerate和map的交互很复杂,所以如果不检查assembly,我不会指望它。
The second one (items[k..]
) uses subslicing, which will be an O(1) operation as it's simply going to index into a chunk of memory. Then you do addition which will also be simple.
第二个(项[k.. .])使用子分段,这是一个O(1)操作,因为它只是将索引放入一个内存块。然后做加法,这也很简单。
However, the only true test is to do some profiling. We will create a large input array and start part way:
然而,唯一真正的测试是做一些分析。我们将创建一个大的输入数组并开始部分方式:
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());
}
Running that code, compiled with optimizations, has the following times averaged over 10 runs:
运行该代码并通过优化编译,其平均运行时间为10次:
- 153.6ms
- 153.6毫秒
- 160.1ms
- 160.1毫秒
So, contrary to what my gut feeling said, the first version is faster. It's entirely possible that my benchmarking is incorrect, but that's why you should do benchmarking on your real code.
所以,与我的直觉相反,第一个版本更快。我的基准测试完全有可能是不正确的,但这就是为什么您应该对真正的代码进行基准测试。
Also, note that this is really fast either way. That's about 15 or 16 nanoseconds per item. What's one nanosecond among friends?
另外,请注意这两种方法都非常快。每一项大约15或16纳秒。朋友之间的一纳秒是多少?
#1
4
Let's use this code as an example. It's similar to your example, but a bit simpler:
让我们以这个代码为例。它类似于你的例子,但更简单一点:
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);
}
Which will be more performant is a tricky topic. Rust and LLVM have good optimizations that can make code quite fast.
这将是一个棘手的话题。Rust和LLVM有很好的优化,可以使代码非常快速。
Based purely on my gut feeling, I would probably use the first one if I knew that I was going to skip just "a few" of the items, and the second one if I didn't know how many or if it were a lot.
纯粹基于我的直觉,如果我知道我将跳过“一些”的条目,如果我不知道有多少,如果我不知道它有多少,我可能会使用第一个。
In the first case, you conceptually have to iterate through all of the items that you want to skip over. It is possible that the optimizer could reduce this down, but it's complicated by the interaction with enumerate
and map
, so I wouldn't count on it without inspecting assembly.
在第一种情况下,您必须在概念上遍历您想要跳过的所有项。优化器可能会减少这一点,但由于与enumerate和map的交互很复杂,所以如果不检查assembly,我不会指望它。
The second one (items[k..]
) uses subslicing, which will be an O(1) operation as it's simply going to index into a chunk of memory. Then you do addition which will also be simple.
第二个(项[k.. .])使用子分段,这是一个O(1)操作,因为它只是将索引放入一个内存块。然后做加法,这也很简单。
However, the only true test is to do some profiling. We will create a large input array and start part way:
然而,唯一真正的测试是做一些分析。我们将创建一个大的输入数组并开始部分方式:
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());
}
Running that code, compiled with optimizations, has the following times averaged over 10 runs:
运行该代码并通过优化编译,其平均运行时间为10次:
- 153.6ms
- 153.6毫秒
- 160.1ms
- 160.1毫秒
So, contrary to what my gut feeling said, the first version is faster. It's entirely possible that my benchmarking is incorrect, but that's why you should do benchmarking on your real code.
所以,与我的直觉相反,第一个版本更快。我的基准测试完全有可能是不正确的,但这就是为什么您应该对真正的代码进行基准测试。
Also, note that this is really fast either way. That's about 15 or 16 nanoseconds per item. What's one nanosecond among friends?
另外,请注意这两种方法都非常快。每一项大约15或16纳秒。朋友之间的一纳秒是多少?