On a loop of 10000 integers it’s 6 times faster on my machine!
Rust has some specialized implementations for vec.into_iter().collect() that avoid reallocations, but in this case this could be a measurement error.
10K integers doesn’t sound like much. In release mode it could run too quickly to be reliably measured. Test runs that take microseconds are too easily affected timer resolution, rare costs of memory allocator init/growth, warmups of various CPU caches. Quick operations need to be measured with bencher or criterion.
OTOH if you didn’t run it with --release, then the default debug mode would be 10x-100x slower, with wildly varying runtime speeds, depending on very uninteresting irrelevant details that are designed to be removed in the --release mode.
I used cargo bench with the divan benchmarking library to measure this. Do you think there could be a measurement error with this? It’s also possible I need to drop in black_box somewhere, though the difference in performance at least suggests different work is being done. I’ll play around with that a bit more.
I don’t know too much about Rust, but generally, instead of doing X once and trying to measure the time it takes with great precision, you can do X many times and measure how long that takes.
Since my previous post on the Humble For loop in JavaScript got some enjoyable discussion here I figured I’d share this one too. I hope people enjoy it. I hope I didn’t get too much wrong; I present this to Rust wizards with some trepidation.
Performance aside, these articles remind me of something a professor told me years ago. “You’re being clever again, be clear, not clever.” A lot of times the attempts to cram everything into a single line of Functional Perfection™ feel like programmers trying to be clever.
Yes, I’m trying to communicate heuristics; often the for loop combines readability with performance, in particular when you’re constructing collections of a different size out of other collections.
I like declarative code just fine, and it doesn’t have to be that clever. But code that just loops and mutates has an important place.
I’ve since posted an update to this article (at the bottom, you may need a reload), as a comment got me thinking. I had originally excluded the case where you do accumulator.extend(list), because in the JavaScript article I argued that this is harder to reason about and breaks strict functional rules.
But that’s not true for Rust; it passes in a moved Vec as the accumulator, and thus we know there are no other references to it and it’s entirely safe to modify and return. So that changes the trade-offs somewhat; while I still think the for loop is slightly simpler and easier to modify, there isn’t much to say against the fold solution with extend either.
Rust has some specialized implementations for
vec.into_iter().collect()that avoid reallocations, but in this case this could be a measurement error.10K integers doesn’t sound like much. In release mode it could run too quickly to be reliably measured. Test runs that take microseconds are too easily affected timer resolution, rare costs of memory allocator init/growth, warmups of various CPU caches. Quick operations need to be measured with bencher or criterion.
OTOH if you didn’t run it with
--release, then the default debug mode would be 10x-100x slower, with wildly varying runtime speeds, depending on very uninteresting irrelevant details that are designed to be removed in the--releasemode.I used
cargo benchwith the divan benchmarking library to measure this. Do you think there could be a measurement error with this? It’s also possible I need to drop in black_box somewhere, though the difference in performance at least suggests different work is being done. I’ll play around with that a bit more.I don’t know too much about Rust, but generally, instead of doing X once and trying to measure the time it takes with great precision, you can do X many times and measure how long that takes.
(You might be doing this already, not sure.)
Yes, that’s what Divan is doing, so that should be all right.
I’ve now published the benchmark code itself:
https://github.com/faassen/flatten-rust
Since my previous post on the Humble For loop in JavaScript got some enjoyable discussion here I figured I’d share this one too. I hope people enjoy it. I hope I didn’t get too much wrong; I present this to Rust wizards with some trepidation.
Performance aside, these articles remind me of something a professor told me years ago. “You’re being clever again, be clear, not clever.” A lot of times the attempts to cram everything into a single line of Functional Perfection™ feel like programmers trying to be clever.
Yes, I’m trying to communicate heuristics; often the for loop combines readability with performance, in particular when you’re constructing collections of a different size out of other collections.
I like declarative code just fine, and it doesn’t have to be that clever. But code that just loops and mutates has an important place.
I’ve since posted an update to this article (at the bottom, you may need a reload), as a comment got me thinking. I had originally excluded the case where you do
accumulator.extend(list), because in the JavaScript article I argued that this is harder to reason about and breaks strict functional rules.But that’s not true for Rust; it passes in a moved
Vecas the accumulator, and thus we know there are no other references to it and it’s entirely safe to modify and return. So that changes the trade-offs somewhat; while I still think the for loop is slightly simpler and easier to modify, there isn’t much to say against the fold solution with extend either.[Comment removed by moderator pushcx: Comment posted to wrong topic, see https://lobste.rs/c/t8fgll]
[Comment removed by moderator pushcx: Pruning meta thread about misdirected comment.]
[Comment removed by moderator pushcx: Pruning meta thread about misdirected comment.]
[Comment removed by author]
[Comment removed by moderator pushcx: Pruning meta thread about misdirected comment.]