8 ms·
I am the the original author, and trust me, I am describing a real world problem. I run massive parallel data processing tasks on machines with 128 cores, and u
by tneumann 5y ago
I am the the original author, and trust me, I am describing a real world problem. I run massive parallel data processing tasks on machines with 128 cores, and unfortunately some of them produce errors deep within the processing pipeline. From a programming perspective exceptions would be ideal for that scenario, but they cause severe performance problems.
Just think about this: If you have a 100 cores, and 1% of your tasks fail, one core is constantly unwinding. And due to the global lock you quickly get a queue of single threaded unwinding tasks. And things become worse, we expect to have machine with 256 cores soon, and there it is even more dangerous to throw.
If you do not believe me look here: http://wg21.link/p0709 http://wg21.link/p0709 It lists quite a few applications that explicitly forbid exceptions due to performance concerns.
- jcelerier 5y ago> I am the the original author, and trust me, I am describing a real world problem. for a more-or-less niche part of "real world". The overwhelming majority of desktop GUI apps rely on some C++ system - Qt, gtkmm, Wx, Blink, Gecko, FLTK, etc etc... and it is not an issue for those, for what exceptions are commonly used for (a write failing because the user disconnected the USB drive while it was copying, a system resource limit exhausted.. things like that). As much as massive parallel data processing tasks matter, I'd really prefer my language to not side-step writing end-user apps for something that happens at $bigcompany or $bigresearchlab. here's the list of binaries that link against libstdc++.so.6 in my /usr/bin: https://paste.ofcode.org/gfZJwx4puVx7Uxy9a7BBU3 https://paste.ofcode.org/gfZJwx4puVx7Uxy9a7BBU3 - don't forget those please :)
- mcguire 5y agoAre you arguing that C++ is unsuitable for massive parallel data processing tasks?
- garbagecoder 5y agoNo, he's arguing that he'd prefer it not be if that means breaking support for end user apps which is what the OP is arguing for.
- PaulDavisThe1st 5y agofor ones that throw an exception for 1% of a unit computation, perhaps yes. But as has been noted, that's a lot of failures and exceptions may not be the appropriate mechanism to deal with this type of failure.
- ncmncm 5y agoIndeed, arithmetic errors are what NaN was invented for. It still works, and is fast.
- jandrese 5y agoI think he's arguing that C++ exceptions are unsuitable for parallel data processing, at least in cases where exceptions are regularly thrown.
- gpderetta 5y agoWell of course, they are slow so they are unsuitable. The issue is whether they need to be slow.
- ncmncm 5y agoThere is the red flag: "regularly thrown". "Regularly" is opposite to "exceptionally".
- jcelerier 5y agoIf exceptions are regularly thrown the software has a bad design and must be fixed. Non-exceptional stuff must of course not be handled through exceptions - no exception should ever be thrown if the software operates as it is expected to.
- jcelerier 5y agoNo, of course not. But let's not change the language in ways that benefits those use cases at the detriment of more common use cases. Of course, if there are ways to keep more or less the same semantics, while increasing performance, by all means it should be done !
- tjungblut 5y agoIf you run into contention issues have you tried scaling horizontally? Running on 128 single core VMs should mitigate what you see.
- CyberDildonics 5y agoThat would turn communicating between threads into communicating between VMs, which is a disastrous change in itself.
- tjungblut 5y agohow do you know the threads need to communicate with each other?
- CyberDildonics 5y agoHow do you know they don't?
- gpderetta 5y agoIt would. On the other hand multiprocessing with explicitly shared memory could be a fast (but potentially painful) workaround.
- CyberDildonics 5y agoThat could make sense for processes, which would also get around the exception unwind lock, but I don't know how that would work with every thread moved into a separate VM.
- gpderetta 5y agoOf course. Using VMs here would very deep into overengineering territory.
- nyanpasu64 5y agoIf you want to avoid a global process lock, multiple processes will have less orchestration and communication overhead than multiple VMs (though still a lot more than necessary with threads within a process).
- CyberDildonics 5y agoAll of this hinges around the global lock, although I'm not sure what you mean exactly. Are you talking about memory allocation, a lock that exception handling takes or something else?
- tneumann 5y agoThe main problem is the unwinding lock. Memory allocation is a bit unfortunate, too, but much less so. Note that I am trying to fix the unwinding problem, I have submitted a patch to libunwind to eliminate the contention during unwinding: https://reviews.llvm.org/D120243 https://reviews.llvm.org/D120243 It require application support, unfortunately, as there is currently no way that libunwind can figure out if a shared library has been added or removed. But if you are willing to indicate that from within the application the performance problem is mostly fixed. The memory allocation issue remains, but I can live with that. I cannot live with single threaded unwinding.
- drmeister 5y agoThanks for that patch - I'll watch this with great interest. Is there a problem if dynamic libraries invoke dlopen/dlclose they also need to call the sync function - correct? I'm asking because we've developed a Common Lisp implementation that interoperates with C++ and it uses exception handling to unwind the stack (https://github.com/clasp-developers/clasp.git https://github.com/clasp-developers/clasp.git). We hit this global lock in unwinding problem a lot - it causes us a lot of grief.
- wvenable 5y ago> and 1% of your tasks fail That's a lot of failures. I'd question whether or not you should be using exceptions for something that happens 1% of the time all the time. Admittedly you might not have any choice in the matter if it's a dependency that is failing.
- infogulch 5y agoThe "exceptions should be exceptional aka rare" line sounds aspirational, but I wonder how true it is if you survey the general landscape of actual libraries.
- IshKebab 5y agoProbably not that rare to use exceptions but I imagine it is very rare to use exceptions for normal flow control - that is, to continue execution after an exception. It's a common pattern in Python but not C++. I've done it exactly once and I ended up removing it because it makes debugging exceptions that you care about really annoying.
- jolmg 5y ago> but I imagine it is very rare to use exceptions for normal flow control - that is, to continue execution after an exception Exceptions are at least a very central part of the normal flow control of parser combinator libraries.
- gpderetta 5y agoYou are not wrong. The exceptional exception guideline feels circular. I would use exceptions significantly more if I could rely on their performance.
- randyrand 5y agoOn top of that, you never know the context in which your users will call your library. Are you encoding a single file on a local machine? Or 1000 files on some HPC 128-core machine? Do we really want to be in a world where all libraries need to be written with and without exceptions since we can't know the context they'll be used in?
- ncmncm 5y agoIf you are seeing 1% of operations failing, I can guarantee you are Doing It Wrong.
- kfitch42 5y agoNot only is this relevant to HPC environments, it also impacts the other end of the spectrum in the embedded space. When dealing with real-time requirements, you are much more concerned with the worst case performance as opposed to the average case or happy path performance. This article makes it very clear that exceptions complicate analysis of performance. The non-local nature of exceptions mean I can't analyze performance in isolation. E.g. I can test that thread 1 always meets its deadlines (even with exceptions being thrown), then I can test that thread 2 always meets its deadlines.... but if thread 1 and thread 2 happen to throw exceptions at nearly the some time I might miss both deadlines. Who knows, maybe this means a thruster fires too long and that insertion burn fails ... and Mars has a new crater instead of a lander. And, once you throw -fno-exceptions you are no longer using standard C++, which the standard library assumes. So, using anything that would throw exceptions on memory allocation failures is a no-go. You can work around this with extensive use of allocators (that reference enough static memory to avoid any possible out-of-memory situation)... but this is not looking like idiomatic C++ anymore, and most off-the-shelf libraries are unusable. A completely local exceptions implementation (e.g. Herbceptions) would solve this.
- ncmncm 5y agoUsing local allocators where they offer some benefit certainly is idiomatic C++. Freeing all objects so allocated by simply reclaiming the blocks from a local allocator is also idiomatic C++. Major subsystems that use no memory except what is passed in from above is also idiomatic C++. C++ is a big tent. Things you do routinely in one part of a program, such as at startup, may be very different from what you do in a main loop, or in termination cleanup. Things my program does may be very different from what your program does.
- deleted 5y ago[deleted]
- zwieback 5y agoYou mention the problem of having to recompile for ABI-breaking improvements. Just out of curiosity - in the environment you're describing, how much of the code cannot be rebuilt? As I was reading I was assuming that most situations where exception handling causes performance issues are probably also situations where you have most if not all the source code available. This wasn't the case in my formative C++ years when 3rd party libs were expensive and distributed as binaries (on floppies).
- Asooka 5y agoI agree with the premise - C++ exceptions are not a good choice for signalling numerical errors in high-performance computing environments, where said errors can happen with some frequency. However, I do not agree with the conclusion - what is the difference between removing exceptions and simply not using them in your project? All compilers already let you compile code without exceptions and the product I work on compiles all numerical code that way. I don't see what benefit you expect to reap from changing the fundamental way C++ exceptions are implemented and work. At the high end, you want a custom error solution fit for your particular task, I don't think we can have a generic exception framework that will work for every high-performance computing project.
- xienze 5y ago> I run massive parallel data processing tasks on machines with 128 cores So, not to be rude here, but this may be a real-world scenario in your world, but not in everyone else’s. I get the sense the kind of stuff you’re working on would benefit from things like using assembly as well. But for desktop apps, games, compilers, other command line tools… who cares about the performance of throwing exceptions?
- jgod 5y agoGames cares about it. But you're right that it's only very high-performance contexts.
- cesaref 5y agoI've worked in low latency trading, and with large distributed back testing setups with hundreds of high core count machines, and not once felt the need to disable exceptions, or for that matter, felt the impact of them happening. A fair bit of noexcept stuff was useful to improve runtime performance, but that was about it. I would suggest you need to address that 1% of failing tasks and determine what the issue is, as frankly, you are solving the wrong problem. If I might suggest a solution, it sounds like you are using threading when process farms might be a better solution. (and if i'm way off the mark with my suggestion, apologies, i'm trying to help and have little information to work with).
- cma 5y agoEven reducing it to .1% we'll still have 1000 cores or at least threads before too long. GPUs already have thousands. Prosumer CPUs already have 128 threads. Epyc supports SMT, and will be 512 threads on dual socket Bergamo in 2023 I think on the roadmap. There is an intermediate at 384 threads dual socket Genoa in 2022.
- nemothekid 5y agoI'm not sure I understand the appeal of C++ exceptions. For years I've been told that Rust and Go's lack of advanced exception support is a crutch; and that error returns or sum types were unimaginative. Now, in this thread, exceptions are supposed to be used rarely. I don't see the difference between an exception and Rust/Go's `panic`. If the error is rare enough, then chances are you cannot gracefully recover. If the error isn't rare; then why are you using exceptions for control flow?
- cesaref 5y agoExceptions have always supposed to be used rarely - that's the point! The most obvious example is 'new' failing and returning std::bad_alloc. Given that new is implicit all over the place (pushing to a vector for example) the code would get very cluttered if you had to cope with this everywhere, so the pragmatic decision was taken to use exception handling to give apps that cared the chance to deal with them, and those that don't a sensible error message by default when the exception escapes main. It's interesting that the lack of enforced exception handling was seen as a strength when exceptions were added to the language, compared to now when the lack of enforced exception handling is sometimes seen as a weakness. Programming language design and expectations are a moving target, so it'll be interesting to see how these things develop over time. And yes, Rust and Go take a different approach. Is there right and wrong? No, i'd say just different approaches which will play to the strengths of some problem domains and coding styles.