Also removes some now-unnecessary docs files. Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4193 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: Lizzie <lizzie@eden-emu.dev>
4.0 KiB
Coding guidelines
These are not stylistic guidelines, they're, for the most part, suggestions on how to architecture new systems or improve upon the existing codebase.
Foreword
Keep your code simple, efficient, and readable.
C++ guidelines
Everyone has their own way of viewing good/bad C++ practices, my general outline:
-
At your disposal you may use
boost::container::static_vector<>(beware it has a ctor/initialization cost which goes up the more elements you add).- Or you may use
boost::container::small_vector<>(which has an initialization cost as well, and will use extra book-keeping for heap, try to keep a balance).
- Or you may use
-
Don't use
[[likely]]or[[unlikely]]; PGO builds exist for that. -
Don't use inline assembly to try to outsmart the compiler unless you're 100% sure the assembly you're writing is actually good.
- And if so, try to restructure your C++ code so the compiler vectorizes it/makes it better
- Or if that fails, use intrinsics instead of raw
asm volatile.
-
Use
std::optional<>instead ofstd::unique_ptr<>if possible.std::unique_ptr<>carries indirection cost due to it being memory allocated on the heap.- It isn't often that objects that contain
std::unique_ptr<>, are allocated on the heap themselves, allocating even more things on the heap seems redundant.
-
Avoid
std::recursive_mutexat all costs.- It's basically implemented as a linked list most of the time and has HEAVY performance penalties.
-
Exploit the fact
std::atomic<uint32_t>/std::atomic<int32_t>is basically free on most arches that matter.- In x86_64, an atomic
uint32_tis basicallymov [m32], r32, which is essentially free/cheap.
- In x86_64, an atomic
-
Avoid template parameters unless you really need them.
- For small inlineable functions this is fine, for more complex ones, please consider the generated assembly.
-
Dont make your own memcpy/memset/strcpy/strncpy/etc.
- Seriously DON'T DO THIS. You will NOT beat the compiler.
- Nor 30 years of writing optimized
mem*. - If your code is slow, don't blame
mem*, blame your code.
-
Try to avoid using
virtualsince vtable indirection has a cost -
Avoid
dynamic_castandtypeidat all costs.- The reason is because the project has
-fno-rttidisabled by default, due to the costs of dynamic polymorphism.
- The reason is because the project has
-
Always copy-on-value for objects with
sizeof(void *) >= sizeof(T) * 2, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual. -
Try using move semantics instead of references, whenever possible.
-
Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
-
Don't save a reference in structures of a parent object, i.e:
struct Child { Parent& parent; void Mehod() { parent.Something(); } };-
Instead you can do the following:
struct Child { void Mehod(Parent& parent) { parent.Something(); } }; -
This reduces the amount of pointers you have lying around, and also works better because of the aforementioned cheapness of parameter functions.
-
Engineering guidelines
Programming, alongside the physical act of writing code, also consists of architecting the code you write into a coherent, maintainable system.
- Try to reduce your usage of dependencies
- Dependencies that are legitimately useful to have are few and far between.
- At the same time, NIHing your own implementations of widely adopted algorithms or standards can be quite subpar.
- For dependencies that are very large but contain something you need, consider cherry-picking the individual files it needs (or writing a smaller version of it)
- Try to rely less on indirection for architecturing systems
- If the underlying HLE kernel emulation requires it, try making a solution that keeps things local
- For example, there isn't a need for file descriptors to each be a pointer, when they could be a fixed table size with elements that may be emplaced at will.
- If the underlying HLE kernel emulation requires it, try making a solution that keeps things local