Not very scientific, but it works better for me than my usual mix of C++11-17.
- Filenames:
module.h/module.cpp. Header first, then implementation. - Include Guards: Simple and unique, matching the header name:
#ifndef AUDIO_H #define AUDIO_H // … #endif // AUDIO_H
- Ordering in Headers:
- Public API functions (
create_…,destroy_…) - Data types and structs
- Internal helpers declared
staticin the .cpp only
- Public API functions (
- Indentation: 4 spaces, no tabs.
- Braces: Always on their own line:
void foo() { // … }
- Line Length: ≤ 100 characters. Break long expressions sensibly on operators.
- Types and Structs:
PascalCase(e.g.,AudioBuffer). - Functions:
snake_case(e.g.,create_audio_buffer). - Variables:
lower_casewith underscores (e.g.,sample_count). - Macros and Constants:
ALL_CAPS(e.g.,MAX_SAMPLE_COUNT). - Member Variables: Prefix with
m_(e.g.,m_samples), but avoid if the code is C-style plain structs.
- Function Headers: Brief one-line description, parameters, return value.
- Block Comments: Use
/* … */for lengthy explanations;//for line comments. - No Redundant Comments: Don’t comment “increments i” next to
i++; reserve comments for design rationale or non-obvious behavior.
- Factory/Destructor Pairs:
AudioBuffer* create_audio_buffer(int sample_count); void destroy_audio_buffer(AudioBuffer* buf);
- malloc/free preferred over
new/deleteto avoid exceptions and simplify failure handling. - Explicit Null Checks: Always verify returned pointers before use.
- No STL Containers: Use raw arrays or hand-rolled pools for predictability.
- Separate Pointer and Size:
void process_samples(float* samples, int count);
- create_*: allocates resources only.
- destroy_*: frees resources only.
- process_*: operates on existing data only.
- Classes and Inheritance: Single inheritance only; pure virtual interfaces allowed.
- Lambdas: Sparingly, for simple callbacks—no
std::function. - constexpr: For compile-time constants (e.g., buffer-size limits).
- static_assert: Validate assumptions (e.g.,
static_assert(sizeof(AudioBuffer) == 16);).
- Exceptions: Disabled (
-fno-exceptions); use error codes instead. - RTTI: Disabled (
-fno-rtti); avoiddynamic_castortypeid. - Templates: No custom template classes; only standard
std::uint32_t, etc. - STL Algorithms/Containers: No
<vector>,<string>, or their algorithms.
- Cache-Friendly Structs
• Organize hot-path data to fit within the Ryzen’s 32 KiB L1 and 512 KiB L2 per core caches.
• Group frequently accessed members together; pad to 64-byte cache lines only when absolutely necessary. - SIMD Alignment
• Align performance-critical buffers to 32-byte boundaries to leverage AVX2 on the 7940HS.
• Useposix_memalign(&ptr, 32, size)for frame-buffer or particle data structures. - Memory Bandwidth
• Spread large read-only tables across the 96 GiB DDR5 channels to avoid saturating a single channel.
• Pre-touch pages during load to avoid page-fault stalls on Linux.
- Fine-Grained Job Queues
• Split work into small jobs (~1–2 ms each) so the 16 hardware threads stay busy without contention.
• Use a lock-free ring buffer per NUMA node to reduce cross-core cache bouncing. - Affinity and Load Balancing
• Pin rendering jobs to one physical core’s thread pair to isolate Vulkan calls on the launch thread.
• Distribute workers and services evenly across the remaining 14 threads. - Vulkan Command Submission
• Record command buffers in parallel on multiple threads, then submit on the dedicated render thread.
• Triple-buffer your per-frame resources (descriptor pools, dynamic uniform buffers) to avoid CPU–GPU synchronization stalls. - Thread Synchronization
• Favor lightweight spin-locks or atomic flags for sub-microsecond critical sections.
• Batch cross-thread wake-ups (e.g.,futexon Linux) to reduce scheduling overhead.
- Return
intorenum: Zero for success, nonzero for failure. - Logging: Use
common->Warningorcommon->FatalErrorfor non-recoverable issues; no exceptions.
- assert() for invariant checks in debug builds only.
- No Release aborts: Code must handle all edge cases gracefully.
- Game Logic: In native C++ only—no reliance on interpreted bytecode for core behaviors.
- Scripting Engine: Reserved for high-level events; minimal performance impact.
- Binary Formats: Preferred for large meshes or textures (avoid startup slowdowns).
- Text Formats: Only for small, frequently edited assets (e.g., scripting, config).
- C-Style APIs: Raw pointers, explicit counts.
- Manual Memory Management:
malloc/free,create_…/destroy_…. - Minimal C++: No exceptions, RTTI, templates, or STL.
- Predictable Structure: One function, one job; rigid naming; no hidden state.
- Performance-Centric: Data locality, simple threading, and compile-time checks.