重复(x).take(n)表现?

时间:2015-10-03 11:40:14

标签: performance rust

我正在寻找一种有效的方式来编写以下函数,如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?

3 个答案:

答案 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。)