THANK YOU! I'm only three minutes into the talk and I love the real-world example you used to demonstrate the importance of programming efficiently, despite hardware improvements. I hope talks like these help improve software standards.
I agree that the STL could offer more specialized containers like Boost's dynamic_bitset (with sbo), small_vector and flat_map. The use case is that most of the time one has to deal with a small set of elements but occasionally you go overboard. Using vectors with O(n) lookup can give you the same problems then.
perfect speech, especially how to correctly use emplace_back(). I am trying to explain two questions from STL src code: 1: "can't use aggregate{} with emplace_back()", because at push_back({...}), the parameter type is determined from the container itself, container::value_type , emplace_back({...}) from independent. 2: "emplace_back("hello") faster than push_back("hello")", emmm a little subtle, i guess delay move entity.
slight issue with the advice to ever pass a std::shared_ptr by const ref.... if you're passing a pointer into something that _doesn't_ take ownership, isn't the correct semantics just to .get() on the shared_ptr and pass in the raw ptr?
plasteredparrot If you want to have a locked down API, then pass by const ref to shared_ptr, otherwise someone in the future might pass a raw pointer and be unsure if ownership transfers.
@@mdunkman In modern code raw pointers should *never* transfer ownership. They should always be considered *non-owning* . There is absolutely no reason to have raw pointers managing object lifetimes these days. The problem with passing smart pointers by *const reference* is you limit your code to one type of *smart pointer* making it hard to adapt in the future. If you just want to use a widget, pass a pointer to the widget. Then it doesn't matter what smart pointers the calling code uses. The calling code can use anything from a smart pointer to a value stack object.
While I agree in theory, I have never come across a code base that was remotely modern; legacy systems are everywhere. Nobody is going to be paid to modernize a million lines of "working" code, so owning raw pointers will continue to exist. If you see a raw pointer, you should never make any assumptions, especially a raw void pointer in an interface class to a C library.
@@mdunkman well, still for your own code you can use the modern style and just encapsulate the raw owners in owning smart pointers - then at least in your code base everything is fine
@@mdunkman agreed, even with a codebase that is modern, it may interact / use libraries like Qt which whilst an excellent library for cross platform UI has it's own pointer ownership which passes raw pointers around to transfer ownership.
- 11:00, the last issue is not an actual problem: you can code an array as a limited capacity vector, pushing and popping elements to/from it. - 12:40, what is this member order? The order I declare the variables, inside a f()? - 52:00, I saw std::vector faster than std::array too! The point was that, in my project, there was a main container, which should be a std::vector (and thus was never changed), and many copies were made from parts of it to other containers, which could be std::vector or std::array, the targets for measurement.
What are we doing wrong.... likely smart pointers and RAII are part of it... I know its good practice but back in 1988 more often than not people allocated their memory upfront and killed it at the end managing it through out the lifetime. Constant Initialization, Scoping, destruction and reference counting have been chipping away at performance but since its considered best practice no one seems to care. Use smart pointers, there is NO overhead they say, ok, try it... test it. That is not always the case. All that said, if I had to pinpoint one main cause of the performance issues he is noting I would have to point my fingers at things like Garbage collection and JIT compilers and the most damning thing of all... Fucking Telemetry. It seems like every program believes now that it needs to phone home 24/7 and report back what the user is doing.
@@seditt5146 Don't think that GC, JIT or Telemetry have anything to do that programs can't keep up with typing. I'm still employing techniques from the mid 1990ths. Especially to save memory space. If you save memory you save performance. If you have little data it all doesn't matter but keeping millions of items even saving 100 bytes per object matters for cache efficiency.
@@llothar68 You just stated you don't thin JIT, Telemetry and Garbage Collection do not have anything to do with it yet contradict in the very next paragraph by correctly stating if you save memory you save performance and all of the above things you mentioned take up large amount of memory. GC does not allow on demand memory management such as back in the day, you allocate stuff and it sits there until it is recovered. The Cache Efficiency is also a big deal, matter fact in terms of performance, it is the biggest deal. When useless things like Telemetry constantly phoning home for stuff that serves no real purpose for the users and generally is more for the corporations gain than your own we have a big problem. Esp if every so many m/s it is shitting on the Cache and ruining and coherency you have. Overall it is wastefulness and poor coding, as well as people quickness to use lazy languages like Python, Javascript and Java etc, ones that handle everything for you instead of you handling it. It is a prime example of when you attempt to please everyone.... You please no one. C++ is different in that honestly it seems to attempt to please no one lol, they just want to satisfy ones ability to program... not so much please someone and make it easy.
What are we doing wrong? Quoting Knuth incorrectly. The full quote ends with : "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%." Don't ignore that critical 3%!
Quite the opposite. Most of these problems are caused by C++ being able to do so many things that Rust forbids. A lot of the time they are dangerous, but they can allow for performance gains that rust will not let you get. The entire idea of non const references is totally absent in rust for example, so you're stuck with moving in and out of functions.
@@joestevenson5568Rust absolutely has non-const references. `&mut`. They're safe, thanks to the borrow-checker. Runtime borrow checking and even unsafe references are also available; Rust just tends to make it easier to do the right thing, and sometimes much harder to do the wrong thing.
THANK YOU! I'm only three minutes into the talk and I love the real-world example you used to demonstrate the importance of programming efficiently, despite hardware improvements. I hope talks like these help improve software standards.
In the words of mike akton: People thinking the Performance doesn't matter are the reason I need to wait 15 seconds for Word to load
I agree that the STL could offer more specialized containers like Boost's dynamic_bitset (with sbo), small_vector and flat_map. The use case is that most of the time one has to deal with a small set of elements but occasionally you go overboard. Using vectors with O(n) lookup can give you the same problems then.
"Sadly we don't have an SSO vector"
Yeah we do, it's called basic_string.
"avoid cache misses" shouldn't this be as common as "avoid division by zero" at this point?
It should be. But sadly it's not.
@@dominichofer136 hi, who are you? I'm george
perfect speech, especially how to correctly use emplace_back().
I am trying to explain two questions from STL src code:
1: "can't use aggregate{} with emplace_back()", because at push_back({...}), the parameter type is determined from the container itself, container::value_type , emplace_back({...}) from independent.
2: "emplace_back("hello") faster than push_back("hello")", emmm a little subtle, i guess delay move entity.
slight issue with the advice to ever pass a std::shared_ptr by const ref.... if you're passing a pointer into something that _doesn't_ take ownership, isn't the correct semantics just to .get() on the shared_ptr and pass in the raw ptr?
plasteredparrot If you want to have a locked down API, then pass by const ref to shared_ptr, otherwise someone in the future might pass a raw pointer and be unsure if ownership transfers.
@@mdunkman In modern code raw pointers should *never* transfer ownership. They should always be considered *non-owning* . There is absolutely no reason to have raw pointers managing object lifetimes these days. The problem with passing smart pointers by *const reference* is you limit your code to one type of *smart pointer* making it hard to adapt in the future. If you just want to use a widget, pass a pointer to the widget. Then it doesn't matter what smart pointers the calling code uses. The calling code can use anything from a smart pointer to a value stack object.
While I agree in theory, I have never come across a code base that was remotely modern; legacy systems are everywhere. Nobody is going to be paid to modernize a million lines of "working" code, so owning raw pointers will continue to exist. If you see a raw pointer, you should never make any assumptions, especially a raw void pointer in an interface class to a C library.
@@mdunkman well, still for your own code you can use the modern style and just encapsulate the raw owners in owning smart pointers - then at least in your code base everything is fine
@@mdunkman agreed, even with a codebase that is modern, it may interact / use libraries like Qt which whilst an excellent library for cross platform UI has it's own pointer ownership which passes raw pointers around to transfer ownership.
emplace_back returns void though so in the example you'd have to emplace_back and then get reference to the object via back method.
c++20 version of emplace_back returns ref
I can beat 90 wpm. Glad to hear the correction during questions, 120+ would be an impressive number.
Member order didn't occur to me. I assumed the compiler optimized for it. In some contexts it may, but not the few I've checked, ouch.
Was it Errichto who asked the comparison of unordered map and unordered set ?
Most likely no. I doubt he attends such conferences anyways.
- 11:00, the last issue is not an actual problem: you can code an array as a limited capacity vector, pushing and popping elements to/from it.
- 12:40, what is this member order? The order I declare the variables, inside a f()?
- 52:00, I saw std::vector faster than std::array too! The point was that, in my project, there was a main container, which should be a std::vector (and thus was never changed), and many copies were made from parts of it to other containers, which could be std::vector or std::array, the targets for measurement.
This is a treasury.
"What are we doing wrong?". too many programmers, too many programs, too much code.
What are we doing wrong.... likely smart pointers and RAII are part of it... I know its good practice but back in 1988 more often than not people allocated their memory upfront and killed it at the end managing it through out the lifetime. Constant Initialization, Scoping, destruction and reference counting have been chipping away at performance but since its considered best practice no one seems to care. Use smart pointers, there is NO overhead they say, ok, try it... test it. That is not always the case.
All that said, if I had to pinpoint one main cause of the performance issues he is noting I would have to point my fingers at things like Garbage collection and JIT compilers and the most damning thing of all... Fucking Telemetry. It seems like every program believes now that it needs to phone home 24/7 and report back what the user is doing.
@@seditt5146 Don't think that GC, JIT or Telemetry have anything to do that programs can't keep up with typing.
I'm still employing techniques from the mid 1990ths. Especially to save memory space. If you save memory you save performance. If you have little data it all doesn't matter but keeping millions of items even saving 100 bytes per object matters for cache efficiency.
@@llothar68 You just stated you don't thin JIT, Telemetry and Garbage Collection do not have anything to do with it yet contradict in the very next paragraph by correctly stating if you save memory you save performance and all of the above things you mentioned take up large amount of memory. GC does not allow on demand memory management such as back in the day, you allocate stuff and it sits there until it is recovered. The Cache Efficiency is also a big deal, matter fact in terms of performance, it is the biggest deal. When useless things like Telemetry constantly phoning home for stuff that serves no real purpose for the users and generally is more for the corporations gain than your own we have a big problem. Esp if every so many m/s it is shitting on the Cache and ruining and coherency you have.
Overall it is wastefulness and poor coding, as well as people quickness to use lazy languages like Python, Javascript and Java etc, ones that handle everything for you instead of you handling it.
It is a prime example of when you attempt to please everyone.... You please no one. C++ is different in that honestly it seems to attempt to please no one lol, they just want to satisfy ones ability to program... not so much please someone and make it easy.
"avoid cache misses" with no guidance on how to do it.
What are we doing wrong?
Quoting Knuth incorrectly.
The full quote ends with : "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."
Don't ignore that critical 3%!
Rust: _"Look what they need to do to emulate a fraction of my power!"_ XD
That's what Evolution is
Quite the opposite. Most of these problems are caused by C++ being able to do so many things that Rust forbids.
A lot of the time they are dangerous, but they can allow for performance gains that rust will not let you get.
The entire idea of non const references is totally absent in rust for example, so you're stuck with moving in and out of functions.
@@joestevenson5568Rust absolutely has non-const references. `&mut`. They're safe, thanks to the borrow-checker. Runtime borrow checking and even unsafe references are also available; Rust just tends to make it easier to do the right thing, and sometimes much harder to do the wrong thing.