Skip to content

Commit

Permalink
Implement new heuristics
Browse files Browse the repository at this point in the history
  • Loading branch information
gbaraldi committed Jun 26, 2023
1 parent 879f6d4 commit 71afaf8
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 89 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Compiler/Runtime improvements

* The `@pure` macro is now deprecated. Use `Base.@assume_effects :foldable` instead ([#48682]).
* The mark phase of the Garbage Collector is now multi-threaded ([#48600]).
* Updated GC heuristics to count allocated pages instead of individual objects ([#50144]).
* [JITLink](https://llvm.org/docs/JITLink.html) is enabled by default on Linux aarch64 when Julia is linked to LLVM 15 or later versions ([#49745]).
This should resolve many segmentation faults previously observed on this platform.

Expand Down
10 changes: 7 additions & 3 deletions doc/src/devdocs/gc.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ This scheme eliminates the need of explicitly keeping a flag to indicate a full
## Heuristics

GC heuristics tune the GC by changing the size of the allocation interval between garbage collections.
If a GC was unproductive, then we increase the size of the allocation interval to allow objects more time to die.
If a GC returns a lot of space we can shrink the interval. The goal is to find a steady state where we are
allocating just about the same amount as we are collecting.

The GC heuristics measure how big the heap size is after a collection and set the next collection to when the heap size is twice as big as the current size or to the maximum heap size.
The heuristics measure the heap size by counting the number of pages that are in use and the objects that use malloc. Previously we measured the heap size by counting
the alive objects, but that doesn't take into account fragmentation which could lead to bad decisions, that also meant that we used thread local information (allocations) to make
decisions about a process wide (when to GC), measuring pages means the decision is global.

The GC will do full collections when the heap size reaches 80% of the maximum allowed size.
20 changes: 19 additions & 1 deletion src/gc-debug.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#include "gc.h"
#include "julia.h"
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

// re-include assert.h without NDEBUG,
Expand Down Expand Up @@ -1224,7 +1227,7 @@ JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
gc_logging_enabled = enable;
}

void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect) JL_NOTSAFEPOINT {
void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect, int64_t live_bytes) JL_NOTSAFEPOINT {
if (!gc_logging_enabled) {
return;
}
Expand All @@ -1233,6 +1236,21 @@ void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect
full ? "full" : "incr",
recollect ? "recollect" : ""
);

jl_safe_printf("Heap stats: bytes_mapped %.1f, bytes_decomitted %.1f, bytes_allocd %.1f\nbytes_freed %.1f, bytes_mallocd %.1f, malloc_bytes_freed %.1f\npages_perm_allocd %zu, heap_size %.1f, heap_target %.1f, live_bytes %1.f\n",
jl_atomic_load_relaxed(&gc_heap_stats.bytes_mapped)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.bytes_decomitted)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.bytes_allocd)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.bytes_freed)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.bytes_mallocd)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.malloc_bytes_freed)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.pages_perm_allocd),
jl_atomic_load_relaxed(&gc_heap_stats.heap_size)/1e6,
jl_atomic_load_relaxed(&gc_heap_stats.heap_target)/1e6,
live_bytes/1e6
);
double bytes_mapped = (jl_atomic_load_relaxed(&gc_heap_stats.bytes_mapped) - jl_atomic_load_relaxed(&gc_heap_stats.bytes_decomitted) + jl_atomic_load_relaxed(&gc_heap_stats.bytes_mallocd) - jl_atomic_load_relaxed(&gc_heap_stats.malloc_bytes_freed))/1e6;
jl_safe_printf("Fragmentation %f, mapped_bytes %1.f\n", (double)live_bytes/(double)jl_atomic_load_relaxed(&gc_heap_stats.heap_size), bytes_mapped);
}

#ifdef __cplusplus
Expand Down
2 changes: 2 additions & 0 deletions src/gc-pages.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ char *jl_gc_try_alloc_pages_(int pg_cnt) JL_NOTSAFEPOINT
// round data pointer up to the nearest gc_page_data-aligned
// boundary if mmap didn't already do so.
mem = (char*)gc_page_data(mem + GC_PAGE_SZ - 1);
jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_mapped, pages_sz);
return mem;
}

Expand Down Expand Up @@ -180,6 +181,7 @@ void jl_gc_free_page(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
madvise(p, decommit_size, MADV_DONTNEED);
#endif
msan_unpoison(p, decommit_size);
jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_decomitted, GC_PAGE_SZ);
}

#ifdef __cplusplus
Expand Down
Loading

0 comments on commit 71afaf8

Please sign in to comment.