diff --git a/src/GC/Makefile b/src/GC/Makefile index 8a35c31..87a3a83 100644 --- a/src/GC/Makefile +++ b/src/GC/Makefile @@ -18,7 +18,7 @@ file: heap: $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) lib/heap.cpp -h_test: +h_test: static_lib rm -f tests/h_test.out # $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/h_test.cpp lib/heap.cpp lib/profiler.cpp lib/event.cpp -o tests/h_test.out $(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -o tests/h_test.out tests/h_test.cpp lib/gcoll.a diff --git a/src/GC/include/heap.hpp b/src/GC/include/heap.hpp index f1a97b3..cebf89d 100644 --- a/src/GC/include/heap.hpp +++ b/src/GC/include/heap.hpp @@ -7,8 +7,8 @@ #include "chunk.hpp" #include "profiler.hpp" -#define HEAP_SIZE 160 //65536 -#define FREE_THRESH (uint) 0 +#define HEAP_SIZE 65536 +#define FREE_THRESH (uint) 100 #define HEAP_DEBUG namespace GC @@ -69,9 +69,6 @@ namespace GC // Temporary Chunk *try_recycle_chunks_new(size_t size); void free_overlap_new(Heap &heap); -#ifndef HEAP_DEBUG - void add_to_free_list(Chunk *chunk); -#endif public: /** * These are the only five functions which are exposed @@ -85,7 +82,6 @@ namespace GC static void init(); static void dispose(); static void *alloc(size_t size); - static void *alloc_free_list(size_t size); void set_profiler(bool mode); // Stop the compiler from generating copy-methods @@ -93,7 +89,6 @@ namespace GC Heap& operator=(Heap const&) = delete; #ifdef HEAP_DEBUG - void add_to_free_list(Chunk *chunk); void collect(CollectOption flags); // conditional collection void check_init(); // print dummy things void print_contents(); // print dummy things diff --git a/src/GC/include/profiler.hpp b/src/GC/include/profiler.hpp index bd14048..b8661ad 100644 --- a/src/GC/include/profiler.hpp +++ b/src/GC/include/profiler.hpp @@ -43,6 +43,11 @@ namespace GC { std::vector m_prof_events; RecordOption flags; + std::chrono::microseconds alloc_time {0}; + // size_t alloc_counts {0}; + std::chrono::microseconds collect_time {0}; + // size_t collect_counts {0}; + static void record_data(GCEvent *type); std::ofstream create_file_stream(); std::string get_log_folder(); @@ -60,6 +65,7 @@ namespace GC { static void record(GCEventType type); static void record(GCEventType type, size_t size); static void record(GCEventType type, Chunk *chunk); + static void record(GCEventType type, std::chrono::microseconds time); static void dispose(); }; } \ No newline at end of file diff --git a/src/GC/lib/heap.cpp b/src/GC/lib/heap.cpp index b3a2d6b..2060b93 100644 --- a/src/GC/lib/heap.cpp +++ b/src/GC/lib/heap.cpp @@ -2,9 +2,13 @@ #include #include #include +#include #include "heap.hpp" +#define time_now std::chrono::high_resolution_clock::now() +#define to_us std::chrono::duration_cast + using std::cout, std::endl, std::vector, std::hex, std::dec; namespace GC @@ -51,131 +55,6 @@ namespace GC Profiler::dispose(); } - void *Heap::alloc_free_list(size_t size) - { - // Singleton - Heap &heap = Heap::the(); - bool profiler_enabled = heap.profiler_enabled(); - - if (profiler_enabled) - Profiler::record(AllocStart, size); - - if (size == 0) - { - cout << "Heap: Cannot alloc 0B. No bytes allocated." << endl; - return nullptr; - } - - // Try to find a fragmented section to recycle - Chunk *recycle = nullptr; - auto iter = heap.m_free_list.begin(); - while (iter != heap.m_free_list.end()) - { - if ((*iter)->m_size >= size) - { - recycle = *iter; - heap.m_free_list.erase(++iter); - break; - } - iter++; - } - - // If memory fragment was found - if (recycle != nullptr) - { - heap.m_size += size; - - // If fragment is larger than request, split it - if (recycle->m_size > size) - { - auto new_part = new Chunk(size, recycle->m_start); - auto complement = new Chunk( - recycle->m_size - size, - (recycle->m_start + size) - ); - delete recycle; - // TODO: add complement to free_list - heap.add_to_free_list(complement); - - heap.m_allocated_chunks.push_back(new_part); - - return (void *)(new_part->m_start); - } - - heap.m_allocated_chunks.push_back(recycle); - return (void *)(recycle->m_start); - } - - uintptr_t *max_size = (uintptr_t *)(heap.m_heap + HEAP_SIZE); - uintptr_t *new_size = (uintptr_t *)(heap.m_heap_top + size); - - if (new_size <= max_size) - { - auto new_chunk = new Chunk(size, (uintptr_t *)(heap.m_heap_top)); - heap.m_allocated_chunks.push_back(new_chunk); - - if (profiler_enabled) - Profiler::record(NewChunk, new_chunk); - - heap.m_heap_top += size; - heap.m_size += size; - } - - // Only throws if the allocation failed - throw std::runtime_error(std::string("Error: Heap out of memory")); - } - - void Heap::add_to_free_list(Chunk *chunk) - { - Chunk *curr; - auto iter = m_free_list.begin(); - uintptr_t *prev_start = nullptr; - uintptr_t *prev_end = nullptr; - - while (iter != m_free_list.end()) - { - curr = *iter; - - // If the curr chunk is aligned before param - if (curr->m_start + curr->m_size == chunk->m_start) - { - Chunk *merged = new Chunk( - curr->m_size + chunk->m_size, - curr->m_start - ); - iter = m_free_list.erase(iter); - m_free_list.insert(iter, merged); - return; - } - - // If the curr chunk is aligned after param - if (chunk->m_start + chunk->m_size == curr->m_start) - { - Chunk *merged = new Chunk( - curr->m_size + chunk->m_size, - chunk->m_start - ); - iter = m_free_list.erase(iter); - m_free_list.insert(iter, merged); - return; - } - - // If the first chunk starts after param - if (prev_start == nullptr && curr->m_start > chunk->m_start) - { - m_free_list.insert(iter, chunk); - return; - } - - prev_start = curr->m_start; - prev_end = prev_start + curr->m_size; - iter++; - } - - // This is only reachable if the chunk is at the end - m_free_list.push_back(chunk); - } - /** * Allocates a given amount of bytes on the heap. * @@ -187,6 +66,7 @@ namespace GC */ void *Heap::alloc(size_t size) { + auto a_start = time_now; // Singleton Heap &heap = Heap::the(); bool profiler_enabled = heap.profiler_enabled(); @@ -202,6 +82,8 @@ namespace GC if (heap.m_size + size > HEAP_SIZE) { + // auto a_ms = to_us(c_start - a_start); + // Profiler::record(AllocStart, a_ms); heap.collect(); // If memory is not enough after collect, crash with OOM error if (heap.m_size + size > HEAP_SIZE) @@ -218,6 +100,9 @@ namespace GC { if (profiler_enabled) Profiler::record(ReusedChunk, reused_chunk); + auto a_end = time_now; + auto a_ms = to_us(a_end - a_start); + Profiler::record(AllocStart, a_ms); return static_cast(reused_chunk->m_start); } @@ -231,6 +116,9 @@ namespace GC if (profiler_enabled) Profiler::record(NewChunk, new_chunk); + auto a_end = time_now; + auto a_ms = to_us(a_end - a_start); + Profiler::record(AllocStart, a_ms); return new_chunk->m_start; } @@ -306,6 +194,8 @@ namespace GC */ void Heap::collect() { + auto c_start = time_now; + Heap &heap = Heap::the(); if (heap.profiler_enabled()) @@ -325,6 +215,10 @@ namespace GC sweep(heap); free(heap); + + auto c_end = time_now; + + Profiler::record(CollectStart, to_us(c_end - c_start)); } /** diff --git a/src/GC/lib/profiler.cpp b/src/GC/lib/profiler.cpp index 53bba13..ae31f0d 100644 --- a/src/GC/lib/profiler.cpp +++ b/src/GC/lib/profiler.cpp @@ -114,12 +114,26 @@ namespace GC // profiler.m_events.push_back(event); } + void Profiler::record(GCEventType type, std::chrono::microseconds time) + { + Profiler &prof = Profiler::the(); + if (type == AllocStart) + { + prof.alloc_time += time; + } + else if (type == CollectStart) + { + prof.collect_time += time; + } + } + void Profiler::dump_prof_trace() { Profiler &prof = Profiler::the(); prof.m_prof_events.push_back(prof.m_last_prof_event); auto start = prof.m_prof_events.begin(); auto end = prof.m_prof_events.end(); + int allocs = 0, collects = 0; char buffer[22]; std::ofstream fstr = prof.create_file_stream(); @@ -128,11 +142,22 @@ namespace GC { auto event = *start++; + if (event->m_type == AllocStart) + allocs += event->m_n; + else if (event->m_type == CollectStart) + collects += event->m_n; + fstr << "\n--------------------------------\n" - << Profiler::type_to_string(event->m_type) - << "\nTimes:\t" << event->m_n; + << Profiler::type_to_string(event->m_type) << " " + << event->m_n << " times:"; } fstr << "\n--------------------------------"; + + fstr << "\n\nTime spent on allocations:\t" << prof.alloc_time.count() << " microseconds" + << "\nAllocation cycles:\t" << allocs + << "\nTime spent on collections:\t" << prof.collect_time.count() << " microseconds" + << "\nCollection cycles:\t" << collects + << "\n--------------------------------"; } /** diff --git a/src/GC/tests/h_test.cpp b/src/GC/tests/h_test.cpp new file mode 100644 index 0000000..625e36a --- /dev/null +++ b/src/GC/tests/h_test.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include "heap.hpp" + +using std::cout, std::endl; + +struct Node { + int id; + Node *child; +}; + +Node *create_chain(int depth) { + cout << "entering create_chain"; + std::vector nodes; + if (depth > 0) { + Node *last_node = static_cast(GC::Heap::alloc(sizeof(Node))); + last_node->id = depth; + last_node->child = nullptr; + nodes.push_back(last_node); + for (size_t i = 0; i < depth; i++) { + Node *node = static_cast(GC::Heap::alloc(sizeof(Node))); + node->id = depth-i; + node->child = nodes[i]; + nodes.push_back(node); + } + cout << "\nexiting create_chain" << endl; + return nodes[depth]; + } + else + return 0; +} + +void create_array(size_t size) { + int *arr = static_cast(GC::Heap::alloc(sizeof(int) * size)); +} + +void detach_pointer(long **ptr) { + cout << "entering detach_pointer"; + long *dummy_ptr = nullptr; + *ptr = dummy_ptr; + cout << "\nexiting detach_pointer" << endl; +} + +Node *test_chain(int depth, bool detach) { + cout << "entering test_chain"; + auto stack_start = reinterpret_cast(__builtin_frame_address(0)); + + Node *node_chain = create_chain(depth); + if (detach) + node_chain->child = nullptr; + + cout << "\nexiting test_chain" << endl; + return node_chain; +} + +void test_some_types() { + cout << "entering test_some_types" << endl; + auto stack_start = reinterpret_cast(__builtin_frame_address(0)); + std::cout << "Stack start from test_some_types:\t" << stack_start << std::endl; + + long *l = static_cast(GC::Heap::alloc(sizeof(long))); + std::cout << "l points to:\t\t" << l << std::endl; + detach_pointer(&l); + std::cout << "l points to:\t\t" << l << std::endl; + + // Some more dummy values of different sizes, to test stack pointer alignment + int *i = static_cast(GC::Heap::alloc(sizeof(int))); + char *c = static_cast(GC::Heap::alloc(sizeof(int))); + short *s = static_cast(GC::Heap::alloc(sizeof(short))); + cout << "exiting test_some_types" << endl; +} + +int main() { + cout << "entering main" << endl; + using namespace std::literals; + + auto start = std::chrono::high_resolution_clock::now(); + //std::cout << "Value of start: " << start.time_since_epoch().count() << std::endl; + GC::Heap::init(); + GC::Heap &gc = GC::Heap::the(); + gc.set_profiler(true); + GC::Profiler::set_log_options(GC::FunctionCalls); + gc.check_init(); + auto stack_start = reinterpret_cast(__builtin_frame_address(0)); + + Node *root1 = static_cast(gc.alloc(sizeof(Node))); + Node *root2 = static_cast(gc.alloc(sizeof(Node))); + root1 = test_chain(58000, false); + root2 = test_chain(58000, false); + + + gc.collect(GC::COLLECT_ALL); + auto end = std::chrono::high_resolution_clock::now(); + //std::cout << "Value of end: " << end.time_since_epoch().count() << std::endl; + + gc.print_summary(); + gc.dispose(); + + std::cout + << "Execution time: " + << std::chrono::duration_cast(end - start).count() << " ≈ " + << (end - start) / 1ms << "ms ≈ " + << (end - start) / 1s << "s.\n"; + + return 0; +} \ No newline at end of file diff --git a/src/GC/tests/linkedlist.cpp b/src/GC/tests/linkedlist.cpp index 850e4f6..61ab3c4 100644 --- a/src/GC/tests/linkedlist.cpp +++ b/src/GC/tests/linkedlist.cpp @@ -52,22 +52,10 @@ void clear_list(Node *head) } } -void run_list_test1() +void run_list_test() { - Node *list_a = create_list(10); - print_list(list_a); -} - -void run_list_test2() -{ - Node *list_b = create_list(10); - print_list(list_b); -} - -void run_list_test3() -{ - Node *list_c = create_list(10); - print_list(list_c); + Node *list = create_list(10); + print_list(list); } int main() @@ -77,9 +65,8 @@ int main() heap.set_profiler(true); GC::Profiler::set_log_options(GC::FunctionCalls); - run_list_test1(); - run_list_test2(); - run_list_test3(); + for (int i = 0; i < 10; i++) + run_list_test(); GC::Heap::dispose();