7 ms·
pImpl pattern is great for those who don’t care about performance but it’s inappropriate for most header libraries. You wouldn’t want a library that hides the i
by shereadsthenews 8y ago
pImpl pattern is great for those who don’t care about performance but it’s inappropriate for most header libraries. You wouldn’t want a library that hides the implementation of std::vector for example. With a visible implementation the compiler compile e.g. operator[] down one x86 instruction. With a pImpl pattern it will be an indirect function call in all likelihood that will be hundreds of times slower. It can make sense for libraries where every function is really expensive anyway, but it’s ruinous for STL and the like.
- nanolith 8y agoThis depends on what you are building. Don't commit the sin of early optimization. Does a client of the framework you are writing -- which is probably using STL internally -- need a single instruction operation for adding a value for a call that you make less than 0.001% of the time? Optimization is about end results. Apply the Pareto Principle, and don't forget that your users also need to compile your code in a reasonable amount of time.
- shereadsthenews 8y agoThat only makes sense if you are planning to offer two implementations of your library. Which I of course urge you to not do. This article is about the STL headers. The reason std::sort beats the pants off all other languages’ sort routines is because the iterators of every collection, all specializations of swap, and the comparator can all be visible to the compiler. If they weren’t, it would be a lot slower. Premature optimization is not really a thing but foreclosing future avenues of optimization definitely can be.
- nanolith 8y ago> Premature optimization is not really a thing Okay. I'm going to stop this thread right there and take some opportunity to provide some mentoring. I hope you accept this, as it will help in your career. Read this paper. It is a classic. https://pic.plover.com/knuth-GOTO.pdf https://pic.plover.com/knuth-GOTO.pdf
- nikanj 8y agoKnuth wasn't saying "just ignore performance altogether", he was saying "stop making things needlessly complicated for the last bit of juice".
- nanolith 8y agoFor instance, pulling in the STL for an interface header instead of encapsulating these details. :-) No one is claiming that we should ignore performance all together. But, understanding through profiling where performance issues are and designing toward a faster implementation is more important than trying to inline definitions up front.
- Silhouette 8y agoDo you also write all your functions to pass large inputs by value until a profiler says you can pass a const reference? Some implementation details are so well understood that you really don't need a profiler to do what is probably the right thing by default.
- nanolith 8y ago> Do you also write all your functions to pass large inputs by value until a profiler says you can pass a const reference? No, but neither do I pass types that fit in a native integer by const reference because copies should be avoided at all costs. There is always a tradeoff. > Some implementation details are so well understood that you really don't need a profiler to do what is probably the right thing by default. Avoiding any and all indirection at all costs is not one of these. The pImpl pattern, much like virtual methods, function pointers, etc., are each tools. Indirection is a trade-off that is either worth the expense in cache misses or is not. The cost is not so cut and dried as others in the thread have assumed.
- Silhouette 8y agoAvoiding any and all indirection at all costs is not one of these. "Any and all"? Perhaps not. However, I believe the context here was standard library headers. Those are full of small, often-used functions, so avoiding idioms based on indirection such as pImpl is about as close to a black and white rule as you're ever going to find in the programming world.
- nikanj 8y agoOverall user experience, mobile battery life, and many other metrics are really hard to fix by micro-optimizing a few functions. The key to a system that doesn't feel sluggish is being conscious of performance issues when making design decisions. This pendulum swings back and forth, and we went from "every bit counts" madness of the early days, to the polar opposite of "just burn cycles, whatever". Systems where every interaction feels sluggish are a pain to use, and often nearly impossible to refactor for better performance.
- deleted 8y ago[deleted]
- kccqzy 8y agoUsing the pimpl pattern doesn't mean an indirect function call. The function to be called is always known. It's just an extra indirection in the data member. It's cheap. Think of it as Java style memory layout: everything that's not primitive stored in an object is a reference and therefore behind one level of indirection. The performance of Java is acceptance in the vast majority of use cases. Using pimpl will be the same.
- branchless 8y agoThe only advantage of c++ is max perf. If we could skip a beat we couldn't justify using c++.
- pjmlp 8y agoIt is still wins in "portability + expressiveness + safer than C" areas. There are still more platforms with a C++ compiler available than Ada, Java or C# ones, let alone Go, D, Rust, Swift. So if the goal is to make the code available to all platforms, without having to deal with C's lack of safety, then C++ it is.
- Someone 8y ago”It's just an extra indirection in the data member. It's cheap” That extra indirection often means a cache miss. That isn’t cheap. Accessing each item traversed through a pointer can easily halve program speed. Java tries hard to prevent the indirections (local objects may live in the stack, their memory layout need not follow what the source code say, objects may even only exist in cpu registers)
- repsilat 8y agoHmm... if you were a horrible person you could declare a `char[n]` member instead of a pointer. Then you could placement-new the impl in the constructor, and static-assert that `sizeof(impl)>=n`... No more cache misses :-). :-(
- 8y ago
- fpoling 8y agoFor cases when performance matters one can replace the members with a stab of the same size and alignment and cast the the stab to the real defition in the implementation.
- adrianN 8y agoUgh, that violates strict aliasing, does it not?
- fpoling 8y agoI suppose it can, but in practice it works with any sane compiler that reasonably deals with reinterpret_cast and aliasing as long as aliasing requirements for the stub and the real thing are the same. The latter can be enforced with static asserts.
- deleted 8y ago[deleted]