Yoinked over the garbage collector.

This commit is contained in:
Samuel Hammersberg 2023-03-28 14:15:22 +02:00
parent 2aff7a7743
commit 85f31b129b
33 changed files with 2417 additions and 0 deletions

View file

@ -0,0 +1,21 @@
# Benchmarking
free_overlap():
9_000 nodes:
With indexing:
Execution time: 22624 ≈ 22ms ≈ 0s.
Without indexing:
Execution time: 24891 ≈ 24ms ≈ 0s.
90_000 nodes:
With indexing:
Execution time: 693642 ≈ 693ms ≈ 0s.
Without indexing:
Execution time: 712297 ≈ 712ms ≈ 0s.
Linked list test:
50_000 nodes:
With marking all:
Execution time: 13911478 ≈ 13911ms ≈ 13s.
Without marking:
Execution time: 234361 ≈ 234ms ≈ 0s.

26
src/GC/docs/lib/chunk.md Normal file
View file

@ -0,0 +1,26 @@
# chunk.hpp
A chunk struct object is the basic element of what can be
stored on the heap. When `Heap::alloc` is called a
chunk may be created to represent the space of memory
that was allocated on the heap by `alloc`.
## Members
`bool m_marked`: A boolean flag to mark an object during mark/sweep.
`uintptr_t *const m_start`: A constant pointer pointing to the start
address of the memory space that was allocated.
`const size_t m_size`: The size of the memory space that was allocated.
## Constructors
There are three constructors for a chunk. One regular constructor
and two copy constructors.
`Chunk(size_t size, uintptr_t *start)`: Used for creating new chunks in
`Heap::alloc`.
`Chunk(const Chunk *const c)`: A copy constructor used by the profiler
to store chunk data after the initial chunk is deleted.
`Chunk(const Chunk &c)`: A secondary copy constructor used for debugging.

47
src/GC/docs/lib/event.md Normal file
View file

@ -0,0 +1,47 @@
# event.hpp & event.cpp
An event class used by the profiler to track actions
on the heap.
## Members
`const GCEventType m_type`: The type of event recorded.
`const std::time_t m_timestamp`: The timestamp of the event,
initialized to the current time by `std::time(NULL)`.
`const Chunk *m_chunk`: The chunk an event is related to.
For example, in `alloc` when a new chunk is created, a
new event is recorded with the type of `NewChunk` and
`m_chunk` then contains a copied version of that new chunk.
If an event is not related to a chunk this member is initialized
to a nullptr.
`const size_t m_size`: In an `AllocStart` event, this member
stores the amount of bytes requested to `alloc`. Otherwise
this member is initialized to 0.
## Constructors
`GCEvent(GCEventType type)`: Used for creating events that are
independent of a chunk and size (like `ProfilerDispose`).
`GCEvent(GCEventType type, Chunk *chunk)`: Used for creating events
that are connected to a chunk (like `ChunkMarked`).
`GCEvent(GCEventType type, size_t size)`: Used for creating events
that are related to a size (only `AllocStart`).
## Destructors
`~GCEvent()`: Default destructor and also frees the member
`m_chunk` if it's not the `nullptr`.
## Functions
`GCEventType get_type()`: Getter for the type of the event.
`std::time_t get_time_stamp()`: Getter for the timestamp of
the event.
`const Chunk *get_chunk()`: Getter for the Chunk the event
is related to. The chunk data is constant.
`const char *type_to_string()`: Translates the type of the
event to a string.

54
src/GC/docs/lib/heap.md Normal file
View file

@ -0,0 +1,54 @@
# heap.hpp & heap.cpp
## Members
`char *const m_heap`: This is the pointer to the simulated heap which
collection occurs on. It's a byte array with a constant pointer.
`size_t m_size`: The size of bytes that has been allocated on the heap.
`inline static Heap *m_instance`: The singleton instance of Heap. Before
the heap is initialized this is initialized to the null pointer.
`uintptr_t *m_stack_top`: The address of the topmost stack frame which
serves as the stop for scanning the stack. Initialized as the null pointer
but assigned to the correct address in `Heap::init()`.
`bool m_profiler_enable`: The state of the profiler, `true` if the
profiler is enabled, `false` otherwise. It is initialized as `false`.
`std::vector<Chunk *> m_allocated_chunks`: Contains pointers to all
chunks that are allocated on the heap and can be reachable (if
a collection has been triggered previously).
`std::vector<Chunk *> m_freed_chunks`: Contains pointer to
chunks that have been freed, used to try and recycle chunks.
## Constructors
`Heap()`: Default constructor which guarantees to initialize
the `m_heap` pointer and the byte array. Declared private
in accordance with the singleton pattern.
## Destructors
`~Heap()`: Frees the `m_heap` byte array. Declared private
in accordance with the singleton pattern.
## Functions
`static void init()`: Initializes the heap singleton and the member
`m_instance`. Must be called before any calls to `alloc()`.
`static void dispose()`: Disposes the heap singleton which frees
the heap. If the profiler is enabled the profiler is also disposed.
`static void *alloc(size_t size)`: Tries to allocate `size` amount
of bytes on the heap. The allocation is C-style, meaning `alloc()`
returns a `void *` similar to `malloc` and the user should cast
this pointer to an appropriate type. If this function is called with
the argument of 0, it will return the null pointer. This function can throw
runtime errors on two occasions. One if there is not enough memory
on the heap after a collection is triggered, it will throw a runtime
error with the message "Out of memory". The other occasion is when
a collection is triggered and the heap has not been initialized
properly by calling `init()`.
`static void set_profiler(bool mode)`: Enables or disables (`true`
or `false`) the profiler.

View file

@ -0,0 +1,30 @@
# profiler.hpp & profiler.cpp
## Members
`inline static Profiler *m_instance`: The pointer to the profiler
singleton instance.
`std::vector<GCEvent *> m_events`: A vector of events recorded
by the profiler. The contents are always sorted by time.
## Constructors
`Profiler()`: Default constructor, declared private because of
the singleton pattern.
## Destructors
`~Profiler()`: Default destructor, declared private because of
the singleton pattern. This destructor also deletes any events
that were recorded by the profiler to free memory.
## Functions
`static void record(GCEventType type)`: Records an event independent
of a size and a chunk (like `ProfilerDispose`).
`static void record(GCEventType type, size_t size)`: Records an event independent
of a chunk but not a size (only `AllocStart`).
`static void record(GCEventType type, Chunk *chunk)`: Records an event independent
of a size but not a chunk (like `NewChunk`).
`static void dispose()`: Disposes the profiler by dumping a log file of all
events and deleting events to free memory.

83
src/GC/docs/ref-guide.md Normal file
View file

@ -0,0 +1,83 @@
# GC library - reference guide
The Heap class is the core of the library and contains all necessary
functions for using the library. This class exposes four public functions
which are `init`, `dispose`, `alloc`, and `set_profiler`.
To use the library, simply include it as `#include "heap.hpp"` and link
it during compilation. Or you can compile it to a static library using
the target `make static_lib` which compiles everything to an .a file.
It can also be compiled to a shared library if necessary with the target
`make shared_lib` which produces an .so file.
## Quick guide
1. If you want a profiler, call `Heap::set_profiler(true)`. Otherwise this can be skipped.
2. Call `Heap::init()` to initialize the heap before using `alloc` (**crucial**).
3. Use `Heap::alloc()` as you want.
4. At program exit, call `Heap::dispose()` to free up all the memory used.
## Functions
### Heap::init()
When using the library, the user has to, at the start of the program,
call the `void init()` function, which initiates the Heap singleton
and the class member `m_stack_top`. **It is crucial** that this
functions is called from the `main` function of the end program,
as `init` uses the intrinsic function `__builtin_frame_address`
to find the address of the **first** stack frame of the end program.
If the function **is not** called from the `main` function
of the end program, it is not guaranteed that the garbage collector
will collect all objects.
The intrinsic function used is technically unsafe for this use,
but during testing it has only shown to segfault for values greater
than the one used in `init`. If you run into a segfault, please
contact the developers.
### Heap::set_profiler(bool mode)
This function is used to enable or disable the profiler connected
to the Heap. The profiler is primarily used for testing, but can
also be used in general to keep track of the programs history.
This function takes a single boolean as an argument to represent
the state of the profiler. `true` means that the profiler is enabled
and `false` means that the profiler is disabled. This function
can theoretically be called at any time during program execution,
but it's probably a bad idea. It is recommended to call this function
before the call to `init` or at least at before the first call to
`alloc`.
### Heap::alloc(size_t size)
The probably most important function in this library. This function
is called to request memory from the "heap". `alloc` takes a single
argument which is a `size_t` (unsigned long) to represent the amount
of bytes to allocate on the heap. The allocation is C-style, meaning
that alloc returns a `void` pointer similar to `malloc`, which
is then supposed to be cast by the user to a proper pointer. When
`alloc` is called and there is already not enough memory left on
the heap to accommodate the request, a collection is triggered
to free up memory for the allocation. Hence the user does not
need to make their own calls to `free` or manually free up memory.
`alloc` can also return a null pointer, if the user requests to
allocate 0 bytes. This is not recommended.
`alloc` can also throw runtime errors in two cases. The first one
is of there is not enough memory on the heap available after
a collection, which in case the allocation cannot complete.
The second case is during a collection, where the function
`collect` throws a runtime error if the heap is not already
initialized by a call to `init`. Calls to `alloc` can technically
take place without properly initializing the heap, but this is
not recommended.
### Heap::dispose()
This function is used to dispose the heap at the program exit.
If the profiler is enabled, it is also disposed from a call
to `dispose`. When the profiler is disposed, a log file is
dumped containing the events on the heap. If the profiler
is disabled, nothing happens to the profiler during `dispose`.
After the profiler is disposed, the heap is deleted which
frees up all the memory used and deletes (hopefully) all
the remaining objects in memory.