Thank you Anthony for a nice presentation (and a lovely book). It's a shame that there hasn't been a significant proposal for a C++ std actor library, then most of the primitives discussed in this talk would become deprecated.
On the "Barrier Example" slide, it looks like there could be a dead lock if the main thread cancels the worker threads while one of the worker threads is waiting at arrive_and_wait(), while another worker thread is just testing the stop_requested() condition and exits the loop. In that case it seems that the barrier count would then never go to zero and the first worker thread would be stuck at the arrive_and_wait.
Generally good but saying that if u use your own mutex is bad or don't use busy wait not recommended it really depends on the context. Std::mutex or cond var latency on my projects is prohibiting
I'm still puzzled why we should be so pumped about stop tokens. Isn't that the most trivial aspect of the entire topic? Yet more than 5 minutes were allocated to it, instead of saying sg. like "now, and the thing everybody's more or less doing for stopping threads is fortunately also in std, so we can skip the chore of setting it up manually (and possibly getting it subtly wrong)". (Reminds me of an enthusiastic lecture fragment about how awesome `min` and `max` are, as if they were the pinnacle of algorithm design. Don't get me wrong, I'm a frugalist, I love minimal, elegant designs. But... you know what I mean.) Thanks for the comprehensive overview, regardless.
Did some digging, and it seems Anthony has been an active stakeholder in the stop_token design; e.g. an interesting fragment from e.g. P0660r0 (by Josuttis): "Herb Sutter elaborated on the interrupt token proposal as follows: Let me strongly support Anthony on interruption tokens. It is the state of the art, and the only interruption mechanism I know of that has a chance to get consensus. (FWIW, Microsoft also has existing practice to contribute as PPL provides the same, called cancellation_tokens ..." OK, so it seems I _am_ missing something, and it may not be so trivial after all. But then (as I don't think I'm the only one puzzled; I've seen similar notes on StackOverflow, too), I guess the talk could've focused on illuminating why this innocous-looking piece in the standard is so important (despite appearing to be utterly mundane), and then why it was designed this particular way.
Serious question: why wouldn't I just use OpenMP for this stuff? Standard, well-supported, portable, efficient and - most of all - much simpler for most of these use cases.
Because OpenMP is not in the C++ standard or standard library. Its a compiler extension && library. OpenMP might be made of these primates internally for all we know but this was a talk about what's in the standard (except thread pools) and not what libraries are available that may or may not use these tools.
Well, first of all, it's never easier to use - making out sections and tasks based on pragmas is a headache. It's just unreadable. Secondly, it's not standartized and requires a different library to link.
@@jopa19991 Most of these examples would be far simpler with OpenMP. OpenMP Sections and Tasks are much cleaner and concise. Second, it is so standard that it isn't even a library. It is just built into all the major compilers. No linking or include files required.
I don't see the reason for which jthread should always be preferred over thread; it performs the job very well and if in your logic don't need the stop_execution they're the same thing, and thread can be used in a codebase and projects where c++20 cannot be used. I'd say that instead of prefer jthread in 99% of cases, they're equivalent in 99% of cases, with the 1% remaining where the stop execution logic is needed.
Мне кажется, самое главное это иерархия реализации, например, классов синхронизации. Почти всегда можно использовать старые классы, которые достаточно хорошо оптимизированы и не пытаться всовывать новые классы с сомнительной поддержкой. В рамках иерархии новые классы могут состоять из старых и иметь дополнительные накладные расходы.
I’m moving more and more away from cpp to golang and rust. Cpp is just over engineered at this point, hard to justify it at this point. Why did we design seemingly simple concepts in such a manner?…
C++ is lugging a lot of legacy behind it, not the least of which is that it's a standard with multiple independent implementations, none of which is preferred or canonical. We grumble about legacy, but legacy is the core of stability and compatibility. Long after Rust and Go have gone the way of Perl and Erlang, C and C++ will still be the first (and often only) languages supported on every new CPU architecture or target, because it's the lowest cost and highest value first step, and once you have C++ you have a path to everything important. Your use case will not be abandoned by C++, which is not something you can say about a more nimble language. If you can do it at all, you can do it in C++. And that fact will remain true for C++ longer than any other language. It's as complicated as it needs to be given its constraints, which can be frustrating if you have a short-term horizon. It may not be for you, but it does have its place.
What is over-engineered or complicated? C++ often offers the simplest solution to me, as you don't have to reverse engineer the opinionated choices made under the covers by some fancier language. What you see is what you get. Imho
@@TylerLarson we will say the same thing for rust after 10 or 20 years. But yes, calling c++ overengineered is an insult to such god tier overengeneering tool as java)
Also, with every new update, the ~2000 pages of dense, arcane rules split, proliferate, get reworded, rebased on new concepts etc., become more entangled, more rigid (adding to the legacy burden) etc., so it's been essentially unlearnable for a long time. (I abandoned it a decade ago, after a strong C++-oriented career. Now picking up for hobby projects again (because this one at least I know), and it makes me LOL every day, how fundamentally fucked-up it has become... Just the initialization rules are impossible to remember (let alone keep up with :) )!) Don't get me wrong: it has truckloads of _lovely_ features, it's just, you know... Every time you wanna do something, anything, you have to sink really unpleasantly deep into a shitpool (in full, heavy diving gear, granted, but wearing it is a taxing chore alone), and you'll never resurface again, because as soon as you finally solve something (that's usually simple in other, modern languages), you've introduced a whole bunch of other supporting implementation dependencies that you'll now also need to take care of forever...
You do not have to adopt the new parts into your older codebase. You just need to pick a standard and stay there for around five years and start updating once something breaks.
That's a really great summary of threading work in modern C++!
Really nice presentation... Really clear explaination
Great overview of the various sync primitives.
Just awesome! Thanks CppCon for sharing. I love C++ and its great fantastic community!
Excellent talk.
Thank you Anthony for a nice presentation (and a lovely book). It's a shame that there hasn't been a significant proposal for a C++ std actor library, then most of the primitives discussed in this talk would become deprecated.
Awesome talk by Anthony! cheers
Does anybody know if he is working on the 3rd edition of his book?
thanks for the great presentation
thanks for such great summary
Thank you Anthony
On the "Barrier Example" slide, it looks like there could be a dead lock if the main thread cancels the worker threads while one of the worker threads is waiting at arrive_and_wait(), while another worker thread is just testing the stop_requested() condition and exits the loop. In that case it seems that the barrier count would then never go to zero and the first worker thread would be stuck at the arrive_and_wait.
If I'm understanding it right, all that's missing is an arrive and drop() after the loop or in the destructor?
great talk
All those high level stuff while we are still missing explicit std::spin_lock ; (
fan-fucking-tastic. Great video!
Locking multiple mutexes at 44:44. con.wait, the mutex is unlock while waiting until lambda function is satisfied then lock at 45::00
Generally good but saying that if u use your own mutex is bad or don't use busy wait not recommended it really depends on the context. Std::mutex or cond var latency on my projects is prohibiting
I'm still puzzled why we should be so pumped about stop tokens. Isn't that the most trivial aspect of the entire topic? Yet more than 5 minutes were allocated to it, instead of saying sg. like "now, and the thing everybody's more or less doing for stopping threads is fortunately also in std, so we can skip the chore of setting it up manually (and possibly getting it subtly wrong)".
(Reminds me of an enthusiastic lecture fragment about how awesome `min` and `max` are, as if they were the pinnacle of algorithm design. Don't get me wrong, I'm a frugalist, I love minimal, elegant designs. But... you know what I mean.)
Thanks for the comprehensive overview, regardless.
Did some digging, and it seems Anthony has been an active stakeholder in the stop_token design; e.g. an interesting fragment from e.g. P0660r0 (by Josuttis):
"Herb Sutter elaborated on the interrupt token proposal as follows:
Let me strongly support Anthony on interruption tokens. It is the state of the art, and the only interruption mechanism I know of that has a chance to get consensus. (FWIW, Microsoft also has existing practice to contribute as PPL provides the same, called cancellation_tokens ..."
OK, so it seems I _am_ missing something, and it may not be so trivial after all. But then (as I don't think I'm the only one puzzled; I've seen similar notes on StackOverflow, too), I guess the talk could've focused on illuminating why this innocous-looking piece in the standard is so important (despite appearing to be utterly mundane), and then why it was designed this particular way.
@@lunakid12 Boost.Asio has cancellation tokens as well.
What's wrong with shared_mutex?
And atomic? What is it "last-resort"?
I don't even know Amdahl's law before :)
It's an interesting bit of "in retrospect that should have been obvious" that is sufficiently non-obvious that we gave it a name.
Serious question: why wouldn't I just use OpenMP for this stuff? Standard, well-supported, portable, efficient and - most of all - much simpler for most of these use cases.
I would like to know too? These are available in OpenMP already?
Because OpenMP is not in the C++ standard or standard library. Its a compiler extension && library. OpenMP might be made of these primates internally for all we know but this was a talk about what's in the standard (except thread pools) and not what libraries are available that may or may not use these tools.
Well, first of all, it's never easier to use - making out sections and tasks based on pragmas is a headache. It's just unreadable. Secondly, it's not standartized and requires a different library to link.
*NIH (Not Inventory Here) Syndrome* /s
@@jopa19991 Most of these examples would be far simpler with OpenMP. OpenMP Sections and Tasks are much cleaner and concise. Second, it is so standard that it isn't even a library. It is just built into all the major compilers. No linking or include files required.
Waiting for a std thread pool!
waiting on a condition_variable presumably?
I don't see the reason for which jthread should always be preferred over thread; it performs the job very well and if in your logic don't need the stop_execution they're the same thing, and thread can be used in a codebase and projects where c++20 cannot be used. I'd say that instead of prefer jthread in 99% of cases, they're equivalent in 99% of cases, with the 1% remaining where the stop execution logic is needed.
jthread also does the automatic join on destruction
Мне кажется, самое главное это иерархия реализации, например, классов синхронизации. Почти всегда можно использовать старые классы, которые достаточно хорошо оптимизированы и не пытаться всовывать новые классы с сомнительной поддержкой. В рамках иерархии новые классы могут состоять из старых и иметь дополнительные накладные расходы.
very difficult to listen to >_
I’m moving more and more away from cpp to golang and rust. Cpp is just over engineered at this point, hard to justify it at this point. Why did we design seemingly simple concepts in such a manner?…
C++ is lugging a lot of legacy behind it, not the least of which is that it's a standard with multiple independent implementations, none of which is preferred or canonical.
We grumble about legacy, but legacy is the core of stability and compatibility. Long after Rust and Go have gone the way of Perl and Erlang, C and C++ will still be the first (and often only) languages supported on every new CPU architecture or target, because it's the lowest cost and highest value first step, and once you have C++ you have a path to everything important. Your use case will not be abandoned by C++, which is not something you can say about a more nimble language.
If you can do it at all, you can do it in C++. And that fact will remain true for C++ longer than any other language.
It's as complicated as it needs to be given its constraints, which can be frustrating if you have a short-term horizon. It may not be for you, but it does have its place.
What is over-engineered or complicated? C++ often offers the simplest solution to me, as you don't have to reverse engineer the opinionated choices made under the covers by some fancier language. What you see is what you get. Imho
Nothing better than c++. I enjoy it because of its control over the syntax it gives you. The other languages didn’t implement such this.
@@TylerLarson we will say the same thing for rust after 10 or 20 years. But yes, calling c++ overengineered is an insult to such god tier overengeneering tool as java)
Putting this under a talk about concurrency given the absolute state that is async Rust is kinda funny
C++ has become so annoying. You learn something and three years after, you have to relearn it. Seems like its only job is to tire the userbase.
Also, with every new update, the ~2000 pages of dense, arcane rules split, proliferate, get reworded, rebased on new concepts etc., become more entangled, more rigid (adding to the legacy burden) etc., so it's been essentially unlearnable for a long time. (I abandoned it a decade ago, after a strong C++-oriented career. Now picking up for hobby projects again (because this one at least I know), and it makes me LOL every day, how fundamentally fucked-up it has become... Just the initialization rules are impossible to remember (let alone keep up with :) )!)
Don't get me wrong: it has truckloads of _lovely_ features, it's just, you know... Every time you wanna do something, anything, you have to sink really unpleasantly deep into a shitpool (in full, heavy diving gear, granted, but wearing it is a taxing chore alone), and you'll never resurface again, because as soon as you finally solve something (that's usually simple in other, modern languages), you've introduced a whole bunch of other supporting implementation dependencies that you'll now also need to take care of forever...
You do not have to adopt the new parts into your older codebase. You just need to pick a standard and stay there for around five years and start updating once something breaks.