7 ms·
C and C++ are most often used when performance is the highest priority. Undefined behavior is basically the standards committee allowing the compiler developer
by pogopop77 3y ago
C and C++ are most often used when performance is the highest priority. Undefined behavior is basically the standards committee allowing the compiler developers maximum flexibility to optimize for performance over error checking/handling/reporting. The penalty is that errors can become harder to detect.
It appears the author is a Go advocate. I assume they are valuing clearly defined error checking/handling/reporting (the authors definition of correctness) over performance. If that's what you are looking for, consider Go.
- kelnos 3y agoI think this allowance is a mistake. I suspect that there is some huge number of developer hours that have been wasted, and huge amount of money wasted, on cleaning up after security breaches and finding and fixing security issues. I suspect that those numbers dwarf any losses that might have arisen due to reduced developer productivity or reduced performance when using a (hypothetical) C-like language that doesn't allow the compiler to do these sorts of things.
- PaulDavisThe1st 3y agoThe allowance wasn't "tradeoff performance time with developer time". The point of undefined behavior is "if we specify this, we cause problems for some of the compile targets for this language".
- patmorgan23 3y agoYes, but then complier vendors started abusing UB to increase performance and while silently decreasing safety/correctness. If the compiler creates a security bug by optimizing away a bounds check the programmer explicitly put there, that's a problem. https://thephd.dev/c-undefined-behavior-and-the-sledgehammer-guideline https://thephd.dev/c-undefined-behavior-and-the-sledgehammer...
- jerf 3y ago"a (hypothetical) C-like language that doesn't allow the compiler to do these sorts of things." It's not very hypothetical in 2023. There are plenty of languages whose compilers don't do this sort of thing and attain C-like performance. There isn't necessarily a single language that exactly and precisely replaces C right now, but for any given task where you would reach for C or C++ there's a viable choice, and one likely to be better in significant ways. I also feel like this is missed by some people who defend this or that particularly treatment of a particular undefined behavior. Yeah, sure, I concede that given the history of where C is and how it got there and in your particular case it may make sense. But the thing is, my entire point is we shouldn't be here in the first place. I don't care about why your way of tapping a cactus for water is justifiable after all if you consider the full desert context you're living in, I moved out of the desert a long time ago. Stop using C. To a perhaps lesser but still real extent, stop using C++. Whenever you can. They're not the best option for very many tasks anymore, and if we discount "well my codebase is already in that language and in the real world I need to take switching costs into account" they may already not be the best option for anything anymore.
- spookie 3y agoI'm sorry, but the world doesn't really align with these ideas, sometimes that is. It's understandable, but at the same time it really isn't.
- jerf 3y agoI assume you're referring to "my codebase is already in C/C++"? Which I did acknowledge? Because otherwise, what the world is increasingly not aligning with is using C when you shouldn't be. Security isn't getting any less important and C isn't getting any better at it.
- dahfizz 3y agoI'll stop using C when there is a faster alternative. When nanoseconds count, there is no competition.
- 3y ago
- wheelerof4te 3y agores, err := InterceptNuke(enemyNuke) if err != nil { fmt.Println("NUCLEAR LAUNCH DETECTED!") log.Fatal(err) } else { fmt.Printf("Phew, we're safe. Nuke intercepted after %d seconds.\n", res) } Very clearly defined errors indeed.
- mxmlnkn 3y agoThe undefined behavior I struggle with keeps me from better performance though. I have something like [(uint32_t value) >> (32 - nbits)] & (lowest nbits set). For the case of nbits=0, I would expect it to always return 0, even if the right shift of a 32-bit value by 32 bits is undefined behavior, then bit-wise and with 0 should make it always result in 0. But I cannot leave it that way because the compiler thinks that undefined behavior may not happen and might optimize out everything.
- zzo38computer 3y agoI think that the undefinde behaviour should be partially specified. In the case you describe, it should require that it must do one of the following: 1. Return any 32-bit answer for the right shift. (The final result will be zero due to the bitwise AND, though, regardless of the intermediate answer.) The intermediate answer must be "frozen" so that if it is assigned to a variable and then used multiple times without writing to that variable again then you will get the same answer each time. 2. Result in a run-time error when that code is reached. 3. Result in a compile-time error (only valid if the compiler can determine for sure that the program would run with a shift amount out of range, e.g. if the shift amount is a constant). 4. Have a behaviour which depends on the underlying instruction set (whatever the right shift instruction does in that instruction set when given a shift amount which is out of range), if it is defined. (A compiler switch may be provided to switch between this and other behaviours.) In this case, if optimization is enabled then there may be some strange cases with some instruction sets where the optimizer makes an assumption which is not valid, but bad assumptions such as this should be reduced if possible and reasonable to do so. In all cases, a compiler warning may be given (if enabled and detected by the compiler), in addition to the effects above.
- mxmlnkn 3y agoI wanted to reply that your point 3 should already be possible with C++ constexpr functions because it doesn't allow undefined behavior. But I it seems I was wrong about that or maybe I'm doing it wrong: [[nodiscard]] constexpr uint64_t getBits( uint8_t nBits ) { return BITBUFFER >> ( 64 - nBits ) & ( ( 1ULL << nBits ) - 1U ); } int main() { std::cerr << getBits( 0 ) << "\n"; std::cerr << getBits( 1 ) << "\n"; return 0; } The first output will print a random number, 140728069214376 in my case, while the second line will always print 1. However, when I put the ( ( 1ULL << nBits ) - 1U ) part into a separate function and print the values for that, then getBits( 0 ) suddenly always returns 0 as if the compiler understands suddenly that it will and with 0. template<uint8_t nBits> [[nodiscard]] constexpr uint64_t getBits2() { return BITBUFFER >> ( 64 - nBits ) & ( ( 1ULL << nBits ) - 1U ); } In this case, the compiler will only print a warning when trying to call it with getBits2<0>. And here I kinda thought that constexpr would lead to errors on undefined behavior, partly because it always complains about uninitialized std::array local variables being an error. That seems inconsistent to me. Well, I guess that's what -Werror is for ... Compiled with -std=c++17 and clang 16.0.0 on godbolt: https://godbolt.org/z/qxxWW93Tx https://godbolt.org/z/qxxWW93Tx
- pjmlp 3y agoNowadays, in 1980's.... "Oh, it was quite a while ago. I kind of stopped when C came out. That was a big blow. We were making so much good progress on optimizations and transformations. We were getting rid of just one nice problem after another. When C came out, at one of the SIGPLAN compiler conferences, there was a debate between Steve Johnson from Bell Labs, who was supporting C, and one of our people, Bill Harrison, who was working on a project that I had at that time supporting automatic optimization...The nubbin of the debate was Steve's defense of not having to build optimizers anymore because the programmer would take care of it. That it was really a programmer's issue.... Seibel: Do you think C is a reasonable language if they had restricted its use to operating-system kernels? Allen: Oh, yeah. That would have been fine. And, in fact, you need to have something like that, something where experts can really fine-tune without big bottlenecks because those are key problems to solve. By 1960, we had a long list of amazing languages: Lisp, APL, Fortran, COBOL, Algol 60. These are higher-level than C. We have seriously regressed, since C developed. C has destroyed our ability to advance the state of the art in automatic optimization, automatic parallelization, automatic mapping of a high-level language to the machine. This is one of the reasons compilers are ... basically not taught much anymore in the colleges and universities." -- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work: Reflections on the Craft of Programming
- titzer 3y agoA great quote. She's absolutely right. C has absolutely polluted people's understanding of what compiler optimizations should be. Compilers should make a program go faster, invisibly. C makes optimization everyone's problem because the language is so absolutely terrible at defining its own semantics and catching program errors.
- marcosdumay 3y agoHum... We have to move further than this citation. The 1980s C was much more secure than our current one. The undefined behavior paradoxes were only added by the 90s, when optimizing compilers became a logic inference engine, feed with the unquestionable truth that the developer never exercises UB. Just because it was a sane language for kernel development at the 1980s, it doesn't mean it is one now.
- btilly 3y agoGiven who invented it, Go can be thought of as, "What C might have been if we could have done it." Go really is in many ways more similar to early C in spirit than modern C is.
- anthk 3y agoGo it's modelled after plan9'C [1-9]c = go cross compiling, and Limbo so it has a lot of sense. Both come from the same people after all. Unix->Unix8 -> Plan9 -> Go.
- akshayshah 3y agoExpanding this for those not familiar with Go's history: Ken Thompson, formerly of Bell Labs and co-creator of B and Unix, was deeply involved in Go's early days. Rob Pike, also ex-Bell Labs, was also one of Go's principal designers. I can't find a source now, but I believe Rob described Russ Cox as "the only programmer I've met as gifted as Ken." High praise indeed.
- TwentyPosts 3y agoEh. The existence of Rust (and Zig, to a lesser extent) prove that you can, in fact, have both: Highest performance and safe, properly error checked code without any sort of UB. UB is used for performance optimizations, yes, but all of these difficult to diagnose UB issues and bugs happen because C++ makes it laughably easy to write incorrect code, and (as shown by Rust) this is by no means a requirement for fast code.
- alphanullmeric 3y agoYou can do any rust optimization yourself in C++ (ie. aliasing assumptions), whereas rust makes the other way around very difficult, often forcing you to use multiple layers of indirection where c++ would allow a raw pointer, or forcing an unwrap on something you know is infallible when exceptions would add no overhead, etc. Rust programmers want people to believe that whatever appeases the supposedly zero cost borrow checker is the fastest thing to do even though it has proven to be wrong time and time again. I can’t tell you how many times I’ve seen r/rust pull the “well why do you want to do that” or “are you sure it even matters” card every time rust doesn’t allow you to write optimized code.
- cortesoft 3y agoIsn't that the point of unsafe blocks in rust? So you can write optimized code when you need to and the rust borrow checker won't let you?
- gmueckl 3y agoUnsafe blocks are subject to the same borrow checking that the rest of the language is.
- tialaramex 3y agoThat is correct. However, raw pointers are not borrow checked, in safe Rust they're largely useless, but in unsafe Rust you can use raw pointers if that's what you need to do to get stuff done. As an example inside a String is just a Vec<u8> and inside the Vec<u8> is a RawVec<u8> and that is just a pointer, either to nothing in particular or to the bytes inside the String if the String has allocated space for one or more bytes - plus a size and a capacity.
- tylerhou 3y agoThe author, Russ Cox, was one of the inventors of Go.
- jeremyloy_wt 3y ago> It appears the author is a Go advocate A bit of an understatement. The author is the current Golang project lead and a member since it’s inception
- barsonme 3y agoHe’s more like the BDFL.