jfe*_*ard 5 iterator vector compiler-optimization rust
考虑以下代码:
let u: Vec<u8> = (64..74).collect();
let v: Vec<u8> = u.iter().map(|i| i + 1).collect();
Run Code Online (Sandbox Code Playgroud)
u
没有移动,因此v
不可避免地是新分配的。
但是,如果我执行以下操作:
let w: Vec<u8> = u.into_iter().map(|i| i + 1).collect();
Run Code Online (Sandbox Code Playgroud)
u
被感动了,w
是它改造的名字。这是一些代表我的意思的伪代码:
let u: Vec<u8> = (64..74).collect();
let v: Vec<u8> = u.iter().map(|i| i + 1).collect();
Run Code Online (Sandbox Code Playgroud)
(在我看来)不需要新的分配,因为我们将类型映射到自身。这段代码不会出现这种情况:
let t: Vec<u8> = (64..74).collect();
let s: String = t.into_iter().map(|i| i as char).collect();
Run Code Online (Sandbox Code Playgroud)
总结我的问题
Vec
当我们将 aVec
转换为迭代器,然后将此迭代器映射到相同类型元素上的迭代器,然后将结果收集到 a时,是否有分配 new Vec
?
如果确实有分配,为什么?
我试图--emit=mir
,但我无法找到答案。我每晚都使用 rustc 1.20(如果这很重要)。
让我们看看for的实现来源:into_iter()
Vec<T>
fn into_iter(mut self) -> IntoIter<T> {
unsafe {
let begin = self.as_mut_ptr();
assume(!begin.is_null());
let end = if mem::size_of::<T>() == 0 {
arith_offset(begin as *const i8, self.len() as isize) as *const T
} else {
begin.offset(self.len() as isize) as *const T
};
let cap = self.buf.cap();
mem::forget(self);
IntoIter {
buf: Shared::new(begin),
cap: cap,
ptr: begin,
end: end,
}
}
}
Run Code Online (Sandbox Code Playgroud)
创建IntoIter
迭代器会产生一些额外的分配,但不会用于向量的元素;相反,向量的底层内存细节被注册。怎么样的代码背后map()
?
fn map<B, F>(self, f: F) -> Map<Self, F> where
Self: Sized, F: FnMut(Self::Item) -> B,
{
Map{iter: self, f: f}
}
Run Code Online (Sandbox Code Playgroud)
这里也没有分配额外的向量。最后一块拼图是collect()
:
fn collect<B: FromIterator<Self::Item>>(self) -> B where Self: Sized {
FromIterator::from_iter(self)
}
Run Code Online (Sandbox Code Playgroud)
这里没有答案;怎么样实施的from_iter()
对Vec<T>
?
impl<T> FromIterator<T> for Vec<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Vec<T> {
<Self as SpecExtend<T, I::IntoIter>>::from_iter(iter.into_iter())
}
}
Run Code Online (Sandbox Code Playgroud)
这开始看起来很神奇,但也许相关的SpecExtend 代码会揭示我们正在寻找的内容:
impl<T, I> SpecExtend<T, I> for Vec<T>
where I: Iterator<Item=T>,
{
default fn from_iter(mut iterator: I) -> Self {
// Unroll the first iteration, as the vector is going to be
// expanded on this iteration in every case when the iterable is not
// empty, but the loop in extend_desugared() is not going to see the
// vector being full in the few subsequent loop iterations.
// So we get better branch prediction.
let mut vector = match iterator.next() {
None => return Vec::new(),
Some(element) => {
let (lower, _) = iterator.size_hint();
let mut vector = Vec::with_capacity(lower.saturating_add(1));
unsafe {
ptr::write(vector.get_unchecked_mut(0), element);
vector.set_len(1);
}
vector
}
};
<Vec<T> as SpecExtend<T, I>>::spec_extend(&mut vector, iterator);
vector
}
default fn spec_extend(&mut self, iter: I) {
self.extend_desugared(iter)
}
}
Run Code Online (Sandbox Code Playgroud)
在这段代码中,我们最终可以看到为结果向量分配新空间的Vec::new
和Vec::with_capacity
方法。
TL;DR:不,如果没有额外的分配,就不可能移动和修改向量。
归档时间: |
|
查看次数: |
624 次 |
最近记录: |