我正在寻找一种有效的方式来编写以下函数,如another question中所述:
fn dots(n: usize) -> String {
std::iter::repeat('.').take(n).collect()
}
Rust是否为泛型结构类型的实例生成具体类型? repeat('.').take(n)
(结构Take<Repeat<char>>
)的结果是否等效于
struct Take_Repeat_char {
element: char,
n: usize
}
内联方法实现 - 是否会有Take::next()
版本内嵌Repeat::next()
?
有什么好办法自己检查一下?检查LLVM IR?
答案 0 :(得分:12)
是的,这足够Rusty™。是的,如果您使用优化进行编译(例如cargo build --release
),LLVM将内联整个内容。通过Postal检查并查看生成的程序集。有问题的代码是:
movb $46, (%rax)
movb $46, 1(%rax)
movb $46, 2(%rax)
movb $46, 3(%rax)
movb $46, 4(%rax)
五个点。我相信通过将前四个点合并为一个
可能会更快movd $x2e2e2e2e, (%rax)
指示,但我认为它不会产生太大的影响。编辑:实际上取决于内存对齐,它可能更快或更慢:如果%rax
对齐,它可能会更快一些(取决于复杂的事情,如缓存,预取等) ),否则它可能会更慢(因为可能的陷阱)。
答案 1 :(得分:9)
Rust是否为泛型结构类型的实例生成具体类型?
是的,这称为单态化。
内联方法实现吗?
与许多语言一样,这是一个坚实的“可能”。有hints you can provide the compiler来控制内部和内部的内联,但通常由编译器来做正确的事情。如上所述,如果函数使用泛型类型,它将自动用于单态化,这意味着内联它所需的信息在编译的包中可用。
为自己检查这个有什么好办法?
很多人会使用Rust Playground来查看LLVM IR或程序集。当然,您可以使用rustc --emit [asm|llvm-ir]
在本地查看。在这样做的时候,我把我感兴趣的代码放在一个永远不会内联的函数中。这使得在装配/ IR输出中更容易找到:
#[inline(never)]
fn dots(n: usize) -> String {
std::iter::repeat('.').take(n).collect()
}
正如llogiq已经指出的那样,rustc和LLVM已经看透了整个实现并完全展开了它。实现会根据您想要的字符数进行更改。
唯一方式知道它是否快速是分析。引用llogiq:
我相信通过将前四个点合并为一个
可能会更快movd
我主张在真实世界中测试任何此类代码。汇编非常重要,尤其是x64 / x86_64变体。指令可能有奇怪的管道要求,或者可能导致CPU的其他部分不可用。
个人资料,个人资料,个人资料! ^ _ ^
答案 2 :(得分:5)
它并不是真正的表现,它有点不好。
const CAP: usize = 64 * 1024;
#[bench]
fn fill_string_repeat(b: &mut Bencher) {
b.iter(|| {
repeat('.').take(CAP).collect::<String>()
});
b.bytes = CAP as u64;
}
#[bench]
fn fill_string_vec(b: &mut Bencher) {
b.iter(|| {
String::from_utf8(vec![b'.'; CAP])
});
b.bytes = CAP as u64;
}
结果:
test fill_string_repeat ... bench: 240,467 ns/iter (+/- 719) = 272 MB/s
test fill_string_vec ... bench: 106,885 ns/iter (+/- 224) = 613 MB/s
vec!
解决方案要好得多,包括UTF-8检查开销,它占主导地位,使用String::from_utf8_unchecked
并得到:
test fill_string_vec_unchecked ... bench: 29,354 ns/iter (+/- 503) = 2232 MB/s
(这里基本上只是memset。)