6 ms·
Rust's Vec indexing is bound-checked by default. Just something like this is abysmal for performance (but pretty good for consultants who get called to fix stuf
by jcelerier 5y ago
Rust's Vec indexing is bound-checked by default. Just something like this is abysmal for performance (but pretty good for consultants who get called to fix stuff afterwards, so, like, keep them coming!):
- Rust: https://rust.godbolt.org/z/GK8WY599o https://rust.godbolt.org/z/GK8WY599o
- CPP: https://gcc.godbolt.org/z/qvxzKfv8q https://gcc.godbolt.org/z/qvxzKfv8q
- School-Cotton 5y agoBoth languages let you access vector elements with or without bounds checking. Stroustrup even recommends you use .at by default in c++.
- titzer 5y ago> Rust's Vec indexing is bound-checked by default. Just something like this is abysmal for performance Unless you can provide numbers to back this claim up, I'll continue to rely on my measurements that bounds checking (in Virgil) costs anywhere from 0.5% to 3% of performance. It's sometimes more or less in other languages, but it is by far not the reason any programming language has bad performance. I have no reason to suspect that it costs more in Rust, other than misleading microbenchmarks.
- jcelerier 5y agoHere's a quick and dirty example: https://gcc.godbolt.org/z/3WK7eYM4z https://gcc.godbolt.org/z/3WK7eYM4z I observe (-Ofast -march=native -std=c++20 ; CPU is intel 6900k with performance governor on Arch... blasting at 4GHz) : - clang 13: 8.93 ms per iteration - gcc 11: 9.46 ms per iteration so roughly around 9ms. Replacing return mat[r * matrix_size + c]; by return mat.at(r * matrix_size + c); I observe - clang 13: 20.95 ms per iteration - gcc 11: 18.11 ms per iteration so literally more than twice as slow. I also tried with libc++ instead of libstdc++ for clang and the results did not meaningfully change (around 9ms without bound-checking and 21 ms with).
- titzer 5y agoFrom the godbolt link, it looked like most of the vector operations were not getting inlined. You'll need -O2 or higher to have representative results. I could counter by just writing a Virgil program and turning off bounds check with a compiler flag. We could stare at the machine code together; it's literally an additional load, compare, and branch. The Virgil compiler loves eliminating bounds checks when it can. I know your original comment was in the context of the STL, but it really muddies the waters to see huge piles of code get inlined or not depending on compiler flags. Machine code is what matters. Regardless, this is still microbenchmarking. Maybe matrix multiply is an important kernel, but we need to benchmark whole programs. If I turn off bounds checks in a big program like the Virgil compiler, I cannot measure more than about 1.5% performance difference.
- jcelerier 5y ago> From the godbolt link, I just used godbolt to quickly share the code. On my computer I tried with -Ofast -march=native (broadwell) > I could counter by just writing a Virgil program and turning off bounds check with a compiler flag. We could stare at the machine code together; it's literally an additional load, compare, and branch. This sounds like Virgil is not vectorizing, which makes the comparison much less useful. I see much more than a couple instructions changed there: https://gcc.godbolt.org/z/8jPMb734x https://gcc.godbolt.org/z/8jPMb734x
- titzer 5y ago> Virgil is not vectorizing That's just even more confounding variables. We are, after all, not even doing experiments with Rust vectors, which is what your original comment was about. You wrote examples in C++ and we went down a wormhole already, but I let it slip since at least it was empirical. But I think we shouldn't get distracted with even more side alleys now. Bounds checks are literally just a couple machine instructions, and often eliminated by the compiler anyway, which enables vectorization.
- steveklabnik 5y ago
- colejohnson66 5y agoIf you were using std::vector::at (the proper way), you'd have bounds checking as well. One should only direct index if they really know that they are inside the bounds. And in Rust, there's std::vec::Vec::get_unchecked: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get...
- jcelerier 5y ago> If you were using std::vector::at (the proper way), it is absolutely not the proper way and a complete anti-pattern
- School-Cotton 5y agoThat depends on the project. Anyway, it’s irrelevant, because you can do it either way in either language.
- gavinray 5y agoThis is the Ossia Score lead dev FYI, he's no stranger to either language
- School-Cotton 5y agoI don't know what Ossia Score is. Regardless, I didn't mean to imply anything negative about the competence of the person I was replying to.
- chlorion 5y agoYeah I agree using vector::at is a common and fairly bad anti-pattern. So long as you make sure your program is correct you never need to worry about indices being out of bounds. Requiring bounds checking is a sign you need to eliminate the errors in your software. This makes me wonder, why are people writing software with errors in it in the first place? Even master programmers seem to be too lazy and careless to remove the errors from their software. What gives?
- nivenkos 5y agoYou can just use unsafe if you care in this case though. It's a saner default option tbh.
- steveklabnik 5y agoIdiomatic Rust here would use something like this, not that in this case it makes them performance identical, but it is more representative of real Rust code, and is slightly smaller https://rust.godbolt.org/z/brh6heEKE https://rust.godbolt.org/z/brh6heEKE EDIT: lol don’t write code five minuets after waking, the reply to this has far better code (EDIT: incidentally, I was also curious about a non-extern function, but the compiler is seeing through all of my attempts to call a native Rust function with an opaque body here...) That being said this code will also be a no-op instead of... something worse, if the vector is empty, so, they're not directly comparable, but they weren't really in the first place, as you mention, so...
- School-Cotton 5y agoWhy not for i in &v[1..num] instead of all the iter take skip stuff?
- steveklabnik 5y agoBecause I wrote this comment shortly after waking up and made a mistake. That code is far far better. Thank you.
- amadvance 5y agoC is even better. One instruction less than C++ ;) https://gcc.godbolt.org/z/oc19Gc4eY https://gcc.godbolt.org/z/oc19Gc4eY
- froydnj 5y agoJust because indexing is bounds-checked by default doesn't mean that the bounds-checking is necessarily in your final code: https://rust.godbolt.org/z/er9Phcr3c https://rust.godbolt.org/z/er9Phcr3c Yes, you still have the bounds check when constructing the slice, but that's fine. And since it's probably slightly more common to iterate over the length of the vector anyway: https://rust.godbolt.org/z/e5sdTb7vK https://rust.godbolt.org/z/e5sdTb7vK
- 3836293648 5y agohttps://rust.godbolt.org/z/zM58dcPse https://rust.godbolt.org/z/zM58dcPse And look, putting a limit that the compiler and guarantee once outside the critical loop eliminates it. You're already using unsafe. If you really want to shoot yourself in the foot with this nonsense you can use a get unchecked or an unchecked assumption https://rust.godbolt.org/z/naeaYad5T https://rust.godbolt.org/z/naeaYad5T On top of that, noone would write rust code like that. This version has one bounds check at the beginning and is a lot more idiomatic https://rust.godbolt.org/z/1s9zsMx36 https://rust.godbolt.org/z/1s9zsMx36
- jcelerier 5y agoThe point is to have some code that exhibits what happens when a bound check is needed. You can also do for(double v : std::span(vec).subspan(1)) { } in C++ and that will be safe and not need a bound check except an user-provided one at the creation of the span (-1 for C++ here :-)) - and also not matter at all regarding the bound checks problem which will occur as soon as you don't just iterate linearly which is extremely common in the kind of problems that end up showing at the top of the profiler and the whole point of this conversation.
- imtringued 5y ago>but pretty good for consultants who get called to fix stuff afterwards, so, like, keep them coming!): I'm confused. Doesn't that apply to the C cowboy way of doing things? You introduce security vulnerability after vulnerability and then lots of people have to hire expert security consultants all the time. Your snark just makes no sense to me. Fixing an ArrayOutOfBoundsException in Java is something even a novice programmer with less than a year experience can do. No expensive consultant needed. The Java vulnerabilities aren't even in the same class as C. They are usually quite dumb shit like class loading remote class files using obsolete features that nobody even remembers (log4shell). It's like setting up ssh with a default password (raspberry pi). It happens but it's rare because of the sheer amount of "incompetence" required that it requires lightning to strike twice.
- jcelerier 5y agoI have never come across security consultants. Performance OTOH ...
- astrange 5y agohttps://www.mandiant.com https://www.mandiant.com Sometimes they're called auditors. Or "formal audits" because people think if they call something "formal" it's somehow different from not being "formal".
- jcelerier 5y agoI mean, I know that this is a business that exists, what I mean is that I get called regularly for fixing performance things, but have never heard of any request for security - or met security consultants during my jobs. However I know of a few cases where people would sell members of their extended family in a heartbeat for a % more oomph.