【发布时间】:2020-02-24 22:04:04
【问题描述】:
在 C++ 中,当连接一堆字符串(其中每个元素的大小大致已知)时,通常会预先分配内存以避免多次重新分配和移动:
std::vector<std::string> words;
constexpr size_t APPROX_SIZE = 20;
std::string phrase;
phrase.reserve((words.size() + 5) * APPROX_SIZE); // <-- avoid multiple allocations
for (const auto &w : words)
phrase.append(w);
同样,我在 Rust 中做了这个(这个块需要 unicode-segmentation crate)
fn reverse(input: &str) -> String {
let mut result = String::with_capacity(input.len());
for gc in input.graphemes(true /*extended*/).rev() {
result.push_str(gc)
}
result
}
有人告诉我,惯用的表达方式是单一的表达方式
fn reverse(input: &str) -> String {
input
.graphemes(true /*extended*/)
.rev()
.collect::<Vec<&str>>()
.concat()
}
虽然我真的很喜欢它并想使用它,但从内存分配的角度来看,前者分配的块会比后者少吗?
我用cargo rustc --release -- --emit asm -C "llvm-args=-x86-asm-syntax=intel"反汇编了它,但它没有穿插源代码,所以我很茫然。
【问题讨论】:
-
“单一表达式”形式应该是折叠而不是使用集合
-
Graphemes的迭代器实现有size_hint(),String在其FromIterator实现中使用它来估计缓冲区大小,所以我认为不会有巨大的开销使用collect()。 -
@DenysSéguret 你的意思是像
.fold(String::with_capacity(input.len()), |result, gc| result + gc)而不是.collect::<Vec<&str>>().concat()? -
@DanilaKiver 感谢您对
size_hint发表评论;不知道。内存分配请求/调用的数量会像第一种方法一样吗?我认为对于每个字素簇,由于对应的Vec::push,都会有一个分配,然后是concat的最终分配。我问的原因不是特定于这个玩具示例,我试图了解第二种方法的工作原理。知道它会在更大的项目中有所帮助。 -
@legends2k,在重新阅读
size_hint()实现后,我意识到它使用1作为下边界,并且代码根据提示保留空间也依赖于 lower 界限(String和Vec),所以感觉实际上 will 使用这种特定类型(@ 987654340@).
标签: rust dynamic-memory-allocation