What does this have to do with C++?
What does this have to do with C++?
However I'd also say this is something already well known by most C and, particularly, C++ developers. Yes, you can get better performance by reimplementing much of what C++ gives you, and that is typical of writing C code instead of C++. But there's a fair bit of productivity trade off in doing so.
A few years ago for debugging purposes I wanted to hexdump a large amount of data. Turns out that the standard hexdump tool was pretty slow at it, writing a simple C utility that would format the dump "by hand" instead of using printf was about an order of magnitude faster. You have fully static code instead of the interpreted format string, you can unroll all the formatting, you don't have to rewrite the characters that don't change in every line (spaces, delimiters etc...). It's lightning fast but of course not very flexible.
Yes, of course it is. That's why I took it as an example. Since I work on RealTime systems I usually have to use plain 'write' instead. But that is because 'printf' is an incredibly complex function, not because 'C is slow'.
- printf is a standard function in C's stdlib.
- Because of the nature of the function (using a special purpose format strings and varargs instead of some kind of generic system) the function can't really be aggressively optimized and inlined without compiler magic. This limitation is caused by C's own limitations when it comes to generic programming and type introspection, printf needs to be told explicitly through a "side-channel" (the format string) what types to expect because C lacks the infrastructure to do that by itself. It also means that you can't extend the function for custom types and that you can't type-check the parameters without compiler magic.
- Even though some compilers do implement special magic around printf to perform more aggressive optimizations (like replacing it with "puts" if there's no formatting going on for instance) it's still generally trivial to beat its performance in non-trivial case with handwritten code.
Given all of the above I would say that makes this particular corner of C rather slow indeed. Or at least slower than it might be compared to an hypothetical language that made it easier to optimize string formatting.
int x;
cout << x;
ought to be faster than printf("%d", x), by virtue of knowing the types ahead of time? ios::sync_with_stdio(false);
https://en.cppreference.com/w/cpp/io/ios_base/sync_with_stdi... #include “studio.h”
int x;
printf(“%d”, x);
the compiler knows the types ahead of time, too, so that cannot explain any speed difference (and yes, C compilers do try to avoid the generic printf in the library. See http://www.ciselant.de/projects/gcc_printf/gcc_printf.html for examples)(The #include is essential. With it, the compiler can know what printf does, and, in theory, optimize it to a int-to-string conversion and a puts system call. Without it, it cannot, because it doesn’t know what the printf function does (in this case, it is easier to generate fast code for the compiler if it doesn’t have the source available than when it would see source code for a function called printf)
Utterly pedantic: no. As given, both are undefined behavior, and it is trivial for a compiler to discover that.
That is true for certain format strings (containing only %s or %c conversions), but for %d GCC and Clang don't seem to want to call a print_int function, possibly because there is none: https://gcc.godbolt.org/z/rltFse
I agree with you about the title, but I also think that if you fix the title the author is making a good point. It's not groundbreaking, but the move "down" from C++ to C does run parallel with the move from more general purpose to more special purpose, in a lot of ways. C dominates embedded after all.
It's been one of the things that have most strongly shaped the evolution of the language and the library, for both good and bad, so absolutely, it is very much well known.
You might say that compilation time is nothing.. But it is C++, compilation time has a huge impact on big codebases.
My point stands. The performance trade-offs you're paying for with various C++ features are extremely well understood, and better documented for C++ than most languages.
That doesn't mean there aren't surprises here and there, and obviously there are compiler difference, but the point was that the language is designed so that these tradeoffs are known and documented and for the most part comes as a surprise to people mostly when they don't know the language very well.
It is not a surprise that you can improve on things like std::vector by rolling your own that does exactly what you want; for starters if you know your requirements chances are you can speed up initialization, because you don't need to abide by standard semantics. For starters, if you know you're going to assign a fixed number of elements, you don't need to track size and capacity, and you can often avoid the need for bounds checking.
As for compilation time, that too is very much a matter of paying for what you're using. You can choose to disable debug info, you can choose to not include headers that are bound to result in lots of time spent on parsing templates.
That he's not using many advanced features is irrelevant; his starting point very much had made trade-offs in using functionality that comes with known costs.
Or if performance is basically your only goal (HFT) then you just need to spend this time on a lot of parts of your code.
But then in 99% of code it's a waste of time.
In that case the author is showing what has been widely known by C++ developers for decades. In fact, the "don't use the STL because it's slow" is a mantra which, along with the old "replace STL's default allocators with your customized ones", is repeated ad nauseum in gamedev circles.
It's a variant of the observation that, although in C++ you aren't paying a direct abstraction cost, you are often paying a generalisation cost. This cost is obscured by the abstraction and may not be warranted in your application. In other words, an indirect abstraction cost.
This is more like “is C++’s STL fast for me”.
You could achieve good release build runtime-speeds in modern idiomatic C++, but you have to trade off your compile times and Debug-build runtime-speeds for that.
Well, other than the "zero-cost" features that increase code size. For example careless use of heavy template classes can generate a lot of code.
In other words, in C++ it's very easy to generate a lot of code from seemingly innocent constructs.
Instruction cache misses, page faults (and to lesser amount TLB misses) are not zero cost.
It would be nice to have a tool that could compute the size impact of a given line.
[1] https://gist.github.com/zeux/bf847986e0474cf48f61bb5749da38e...
Given that most new C++ features in the "modern day" are implemented as std::whatever, in the "everything is a library" way, it's extremely relevant.
I still have to google every time I need to sleep the current thread ; and don't get me started on iostream!
However, I'm happy to be able to return a buffer without copying it (move semantics), to precompute frequency tables at compile time (constexpr), to be able to pass around functions without having to create a dedicated class (lambdas), not to have to worry about having an extra conversion (auto) ... And soon, I will be happy to write serialization code without relying on dirty tricks (introspection).
> This is definitely an area where libstdc++ and libc++ could improve in the future - I don’t think it’s reasonable to force users of C headers to pay for the C++ baggage.
On big machines a copying collector with a nursery is much faster and safer if you can deal with the pauses.
Memory safety is still an issue I heard.
#include <cstdio>
void call_func(void(*func)()) {
func();
}
int main(int argc, char* argv[]) {
// Lambda can be converted to a function pointer
call_func([]() {
printf("hello as a function pointer\n");
});
}I am fairly certain I have had error messages and disassemblies that suggest otherwise. This may however be an implementation detail.
> You can use them as just function pointers, too.
Yes, I know this. It only works when there are no captures. And AFAIK once there are captures you start getting object code that looks a lot like you put those captures into an anonymous class. (When not inlined.)
And I still maintain that most people treat class libraries (such as STL) as given and do not inspect how they are implemented and do not consider what constraints / alternatives do exist. One example: the std::dequeue is a dynamic array, and you can't quite tune the size of the inner array easily by means of template or instance parameters - no one cares. Also many people happily use the shared_ptr class and don't even know that it takes very expensive atomic operations to do the reference counting; I would have expected a template parameter that could determine if shared_ptr is meant for multithreaded use or not. Most people don't care and happily use the shared_ptr. Also there are no intrusive linked lists in STL (well, there are in boost)
I can go on and on about the STL (and other widely used libraries if you care) and I still suspect that they get away with these "issues" in a widely used library because people are used not to question library implementations and treat them as frigging black boxes.
And STL really isn't based on an OOP mindeset.