SC::Foundation
The big Segment
refactoring from last month has left so many trails also during March in SC::Foundation
.
Most notably Segment
now:
- Supports custom allocators (WIP)
- Uses relative pointers to store dynamically allocated block information
- Stores its header inline instead of making it part of the dynamic allocation
Custom allocators
- Implementation differs from STL one of embedding allocator in the type, as a template parameter.
- Keeping allocator out of the template parameters allows functions to just accept
Vector<T>&
without needing to care what allocator has been set for it and avoiding the viral effect of needing to make also the method templated around the allocator (that is really bad for compile times). - Allocators are not stored for each single container or dynamic allocation, they're by all means a global.
- They're arranged in a double-linked list and users can push / pop their custom one as needed.
- This makes it easy for example to create strategies where all allocations in a given sub-scope are sliced from a pre-allocated slab of memory that can be later on released all at once.
- Custom allocators must be considered still WIP and their API will likely change. Also documentation about their usage is insufficient and it will be expanded.
Detailed list of commits:
- Foundation: Add FixedAllocator
- Foundation: Add global and thread-local allocators
- Foundation: Add allocation ownership mechanism
- Foundation: Add alignment support to allocators
- Foundation: Add Global and Thread-Local enum
- Containers: Add thread-local Vector and SmallVector containers
- Containers: Use Globals to allocate in ArenaMap
- Documentation: Add initial documentation for Globals
Relative pointers
Relative pointers make it easier to create data structures that can be bulk serialized and restored skipping parsing entirely.
A specific demo featuring this must still be built but this is definitively on the TODO
list.
Relative pointers are quite tricky to implement properly in C++ because it's very easy to trigger UB with this specific class of pointer arithmetics and this has been learned the hard way.
Some tests where failing on GCC (when compiling with -fstrict-aliasing
) but it has been a good occasion to learn about std::launder
and std::start_lifetime_as
family of functions and in general creating optimization barriers with volatile
specifier.
For now relative pointers are used by default but at some point the performance impact due to their use will be assessed.
In case of a significant performance difference maybe it will be re-considered possible to opt-out their usage with some runtime or compile-time flag.
- Foundation: Use relative pointers in Segment and make header inline
- Foundation: Launder Segment::data() returned pointer
- Foundation: Refactor Segment to reduce and isolate UB
Inline header
SegmentHeader
is now part of Segment
declaration. In other words it's no more part of its dynamically allocated part.
This is useful to know all properties of a segment like its inline storage and capacity or the use of custom allocators.
The inline header stores also information about needing to use the Global
or the Thread-Local
allocator.
This helps avoiding unnecessary thread-local access because it's useless if all allocations are known to be done from the main thread (or from a specific thread).
Virtual Memory
A class called VirtualMemory
enables reserving large amounts (max value) of memory and then committing the needed one keeping existing allocation address stable.
This is a big win in many cases because it avoids needing to move everything when a dynamic vector needs to expand, as long as it will be under the max value guessed during the Virtual Memory initialization. This max value can be very large as long as it stays in the allowed virtual memory available range for the given architecture, and in case of 64 bits that's pretty big.
An associated VirtualAllocator
enables using it with the Segment allocator subsystem.
Memory Statistics
All allocations, deallocations and reallocations are now being counted in a Statistics
class providing useful information about how many allocations are done by a given section of code.
The SCTest
is using such statistics to assert when detecting a mismatched number of allocate
vs release
calls.
Vector Growth Factor
This has been also been the occasion to feel ashamed about how many allocations where actually already done leading to make Segment grow by factor of 2
on append.
Debug visualizers
Debug visualizers have been updated to reflect changes in Segment
data layout.
Also a LLDB visualizer for Array<T, N>
was missing and it has been added as well.
And more
Additional minor fixes that don't deserve a whole paragraph to describe them are listed below:
- Foundation: Add no-except to all Segment related methods
- Foundation: Enable creation of Span
- Foundation: Extend Segment assign and insert to types convertible to T
- Foundation: Fix memory Leak in Segment::shrink_to_fit
- Foundation: Split Foundation.cpp content in separate files
- Containers: Move SmallVector to Vector header
SC::Build
SC::Build
has received a small set of changes too.
It's now always using the C++ stdlib on GCC because only more recent GCC versions (> 13) support the -nostdlib++
flag that makes it easy linking libc
but not libc++
or libstdc++
.
Also Makefiles for apple and linux platforms are now created in separate sub-directories. This makes it easier to manually invoke make for them as you don't need the flag to indicate a custom Makefile file name.
This is the detailed list of commits:
- Build: Always link C++ stdlib on GCC
- Build: Create Makefiles in apple / linux subdirs
- Build: Fix enabling standard C++ library for gcc
Minor Changes
And the obvious list of random fixes, with the notable mention of the one related to SC::Async
that was randomly failing in the CI very often due to some bad copy and paste from the io_uring
headers.
- Everywhere: Fixes to compile under ClangCL
- Async: Just fill the entire SQE with zero inside io_uring_prep_rw on Linux
- Async: Set to zer all SQE fields for Linux on io_uring
- Documentation: Fix some typos for build instructions
- SerializationBinary: Fix UB on buffer reader
- Containers: Use placementNew in ArenaMap