Repeats (x) .take (n)?

I am looking for an effective way to write the following function, as discussed in another question :

fn dots(n: usize) -> String { std::iter::repeat('.').take(n).collect() } 

Does Rust create concrete types for instances of generic structure types? It is the result of repeat('.').take(n) (struct Take<Repeat<char>> ), equivalent

 struct Take_Repeat_char { element: char, n: usize } 

Are the implementations of the method implemented inside - will there be a version of Take::next() with Repeat::next() built into it?

What a good way to check this out for yourself? Verify LLVM IR?

+6
source share
3 answers

Yes, this is Rusty ™ enough. And yes, LLVM will integrate all of this if you compile with optimization (e.g. cargo build --release ). Check play.rust-lang.org and look at the generated assembly. This code:

 movb $46, (%rax) movb $46, 1(%rax) movb $46, 2(%rax) movb $46, 3(%rax) movb $46, 4(%rax) 

for five points. I believe that one could go faster by combining the first four points into one

 movd $x2e2e2e2e, (%rax) 

but I don’t think it will make a big difference. Edit: In fact, depending on the alignment of the memory, this may be faster or slower: if %rax aligned, it may be a little faster (depending on complex things like caches, prefetching, etc.), otherwise case it is likely to be slower (due to possible traps).

+12
source

Does Rust create concrete types for instances of generic structure types?

Yes, this is called monomorphization.

Are method implementations implemented?

Like many languages, this is a solid "maybe." There are tips that you can provide the compiler to manage the attachment both inside and inside the box, but usually it depends on the compiler to do the right thing. As above, if a function uses a common type, it is automatically available for monomorphization, which means that the information necessary for its built-in is available in the compiled box.

What is a good way to test this out for yourself?

Many people will use Rust Playground to view IR or LLVM builds. Of course, you can watch it locally with rustc --emit [asm|llvm-ir] . When I do this, I put code that interests me a function that will never be inlined. This greatly facilitates the search for IR collection / output:

 #[inline(never)] fn dots(n: usize) -> String { std::iter::repeat('.').take(n).collect() } 

As llogiq has already noted, rustc and LLVM are already reviewing your entire implementation and have fully deployed it. Changes to the implementation depend on how many characters you want.

The only way to know if it is fast is through a profile. Quoting llogiq:

I believe that one could go faster by combining the first four points into one movd

I would recommend testing any such code in The Real World. The assembly is nontrivial, especially the x64 / x86_64 variants. The instructions may have strange piping requirements or may not be available to other parts of the CPU.

Profile, profile, profile! ^ _ ^

+9
source

It’s not very spectacular, it’s bad.

 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; } 

Result:

 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! solution vec! much better, and this includes the UTF-8 control add-ons that dominate, use String::from_utf8_unchecked and get:

 test fill_string_vec_unchecked ... bench: 29,354 ns/iter (+/- 503) = 2232 MB/s 

(Here basically only memset remains.)

+5
source

All Articles