If you're in an situation where binary size is absolutely critical, then sure. But most people should avoid most of these suggestions.
If you're in an situation where binary size is absolutely critical, then sure. But most people should avoid most of these suggestions.
If people were to listen to your advice, no one would ever post any kind of article on how to achieve certain specific goals, all we'd ever discuss are very generic topics and constantly rehash the same content over and over again. It's good to be able to learn how certain developers manage to accomplish various goals without having someone come along and call that advice terrible.
What are your targets? This was my experience 10 years ago, but today with modern (last 5-6 years) MSVC, GCC and Clang (and even on modern Xbox and Playstation toolchains) my experience has been that it's close enough.
I have been working with small embedded devices so this may be an outlier here compared to the rest of the C/C++ world.
"Write in C instead of C++" is also clearly not the same as "remove all your code"...
I'd much rather use CSS instead of Javascript or going back to the days of hacking <table> to get a certain layout.
Templates and operators overloading are also quite often frowned up.
https://gist.github.com/raizr/c08922b11b33477a1157156e424342...
* No exceptions, ever
* No RTTI, ever
* Compile with -Os
* Limit STL to the non-allocating, non-throwing parts
* Don't allow heap usage
* Link against the C standard library so you don't get accidental heap allocations from lambdas with large capture specs and static construction and destruction fail to link
* Use alternatives to std::function (which while easy is very large and very slow)
* Limit the use of virtual functions (some people suggest eliminating them altogether but I feel like that's a bridge too far).
* Avoid inlining functions if you're not sure that they reduce down to something trivial. I've saved a kilobyte or two by moving some particularly large, commonly used functions into separate translation units so that the compiler couldn't inline them.
* Look at and track your space utilization commit by commit. If the data segment goes up unexpectedly, revisit what you're doing. You may have unwittingly allocated a big block for something. Similar for the code segment.
You get used to the constraints. Type safety, templates, and compile time programming make it 10 times better than C for the purpose in my experience. The only reason to use C is if your C++ compiler is garbage (which is often true for the tiny low powered processors). But if it's ARM you probably have access to a modern compiler and the only reason to stick with C is inertia.
I've been programming with embedded C++ for ten years and every time I have to poke into the C part of the codebase I end up hating life.
Lambdas themselves never require heap allocation. I guess you meant std::function?
Also, in a real-time system context, exceptions can be undesirable since they might cause non-deterministic behaviour.
Catching an exception can be surprisingly costly. Did some benchmarking a while ago on the embedded, real-time system I work on and saw that throwing and catching a std::runtime_error had about the same execution time as a rather slow CRC32 calculation (no pre-calculated tables, no special instructions) of a 256 bytes input array. (Of course, this depends a lot of the CPU architecture, compiler, etc.)
This is in contrast to python or Swift for example, their standard libraries are more “throw-prone”. Building off the previous example Swift’s String.init(contentsOf:encoding:) throws on error on failure.
So in practice, IMO it is usually safe to disable exceptions in C++. Though, I have run into tricky ABI breaks when you link multiple libraries in a chain of exceptions->noexcept->exceptions and so on! You’re of course at the mercy of nonstandard behavior so buyer-beware. I definitely wouldn’t advocate for turning them off -just- for a binary size reduction.
You're just going to end up with an insane amount of error handling only to discover that in the real world, there's likely nothing you can really do anyway.
Using memory that's been allocated but not committed seems like a recipe for disaster.
It can greatly accelerate sparse datastructures.
With exceptions disabled you still get a panic (e.g. the program immediately terminates) in places where an exception is thrown, this should be at least as safe as having exceptions enabled.
> If you don't need IEEE-conformat floating point calculations, use -ffast-math .
I mean… if you know enough to know whether you need standard floating-point calculations, you probably know about the -ffast-math flag.
Someone who is more into system specific stuff might correct me, but I believe fast math produces a binary which can flip bits in the MXCSR if it wants. So, you’ll have to make sure that none of your libraries make any assumptions about floating point behavior, not just your code…
If you just want physics engine go brr and a small binary, though, maybe you don’t.
I find the FP advice suspect for a different reason: I’ve seen some no good, very bad, seriously terrible, really horrifyingly awful codegen from Clang for x86-64 things involving `long double`, and without checking first I wouldn’t assume that it doesn’t extend to all x87 stuff in general.
(Clang’s preferred optimized way to copy a naturally-aligned 16-byte union with a `long double` being the longest member is... to copy the first ten bytes with FLD/FSTP, then the following two bytes of padding by a word-sized MOV, then the final four bytes, also of padding, by a doubleword-sized MOV. Apparently it forgot its MOVAPS at home.
When it spills onto the stack a `long double` temporary it itself invented to implement an atomic operation’s inner loop, though, it first clears the padding using a bleeding XORPS+MOVAPS pair before FSTPing it there—inside the loop, twice, to two identical temporaries, because apparently it belongs to the offshore oil rig school of register spilling.
And so on. I lack the words to describe how bad Clang’s codegen is for anything that might have ever briefly brushed against a `long double`.)
Also take care with -Os; while clang’s -Os is generally sane, gcc -Os will e.g. implement division by a constant with IDIV instead of magic multiplication, giving you code that’s maybe three times shorter and easily a dozen or two dozen times slower. (I think I remember Torvalds saying years ago that he gave up on gcc -Os.)
Is that still true? Might warrant a bug filed on clang/llvm.
So I’d need to do some work to make a small reproducer; then I’d have to figure out how to get access to their bug tracker, because apparently letting people register their own accounts is too easy, let alone just send mail to an address. I don’t know if I have that in me.
(On a more positive note, do you know that sometimes, if you add `default: __builtin_unreachable();` to your switch-threaded interpreter loop, Clang will look at it and replicate the dispatch at the end of each bytecode instruction, like in the standard technique for better branch prediction in handwritten assembly-language interpreters? When I noticed that I couldn’t stop imagining it going “It looks you’re writing an interpreter. Would you like some help?”.)
Also don't go too far with the build flags or you risk waking the sleeping horrors lurking in the seldom tested dark corners of the compilers and linkers we all rely on e.g. accidentally changing the effective ABI through tricks like smaller enums. Some of those bugs may only trigger on certain architectures e.g. exception handling works differently on amd64 and aarch64. On one of the two uses special an extra section to locate the unwind helpers. Do you know the all implications of removing a section from an executable? sigh
Please keep bloat down, but eager developers shouldn't too far beyond the point of quickly diminishing returns within a given ecosystem they're targeting.
Not really, the STL is an accumulation of mediocre design decisions (mostly because it needs to be "everything to everybody"), which can't be fixed because of backward compatibility requirements. In many situations it's actually better to write your own specialized helper classes.
The only time in my entire career i've ever used the STL was in college. Since most C++ jobs are not Appdevs. All those have moved to C#, Go, or some other higher level language. Or again, just rely on the built in suite like RTL, Posix, Unreal
It would be terrible advice if it were framed as "do all these things."