Real-time C++ Programming
09 Jul 2022
[
c++
development
memory
debug
performance
]
Use cases
- High-frequency trading
- Embeddded devices
- Video games
- Audio processing (1-10ms)
“Real-time” programming
- On a normal, non-realtime OS kernel (Windows, macOS, iOS, Android)
- Cross-platform (portability!)
- On a normal consumer machine
- Using a normal C++ implementation (msvc, clang, gcc)
- Only parts of the program are subject to “real-time” constaints, others (e.g. GUI) are not
“Real-time safe code”
- The worst-case execution time is:
- deterministic
- (in principle) known in advance
- independent on application data
- Shorter than the given deadline
- The code does not fail
- Don’t call anything that might block (non-deterministic execution time + priority inversion!)
- don’t try to acquire a mutex
- don’t allocate / deallocate memory
- don’t do any I/O
- don’t interact with the thread scheduler
- don’t do any other system calls
- Don’t call any 3rd party code if you don’t know what is is doing
- Don’t use algorithms with > O(1) complexity
- Don’t use algorithms with amortised O(1) complexity
“Real-time safe”
- No locks
- No OS calls
- No allocations / deallocations
- No exceptions
- No I/O
- No algortihms > O(1)
“Real-time safe” parts of the C++ standard library
- The standard says nothing about the execution time
- The standard does not say “does not allocate memory”
- Infer from specifications
- Check “might invalidate iterators”, “if there is not enough memory”
- The standard does not say “does not use locks”
- “may not be accessed from multiple threads simultaneously”
- “May not introduce data races”
Ecexptions are not “real-time” safe.
- Enter and leave a try block when no exception is thrown is “real-time” safe.
“Real-time safe” STL algorithms
- Element type / iterator type are “real-time safe”.
- Not mentioned by standard.
- For most of them optimal implementation does not require additional allocations.
Not “Real-time safe”
“Real-time safe” STL containers
STL containers with custom allocators
- general purpose (tcmalloc, rpmalloc) are not “real-time safe”
- “real-time safe” allocator
- constant time
- single threaded
- only use memory allocated uprfornt
- Consider
Better: static_vector
- smaller
- faster (no indirection)
- no need to construct allocator object outside vector
- Boost implementation
“Real-time safe” utilities
Not “Real-time safe” utilities - use type erasure
Lambdas
Coroutines
- Not “Real-time safe”
- Possible dynamic allocation
Any syncronization primitives except std:
- Use on its own
- Lock-free queues
- Spinlocks
- Make sure it is lock-free! ([std::atomic::is_always_lock_free](https://en.cppreference.com/w/cpp/atomic/atomic/is_always_lock_free))
Random number generators
- std::rand - Not “Real-time safe”
- Others too - amortised complexity
- Xorshift - maybe, not in standard
- Algorithms for distributions - implementation defined, not “Real-time safe”
References