Skip to content

Commit

Permalink
alloc_custom_mem: do not convert memory size to heap proportion and l…
Browse files Browse the repository at this point in the history
…ater back to work units using a different heap size. When the heap is growing, this unduly accelerates the major GC, slowing down the program.
  • Loading branch information
damiendoligez committed Oct 20, 2023
1 parent 7b2c759 commit a7cd0c6
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 13 deletions.
2 changes: 2 additions & 0 deletions runtime/caml/custom.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ extern "C" {
#endif


CAMLextern uintnat caml_custom_major_ratio;

CAMLextern value caml_alloc_custom(const struct custom_operations * ops,
uintnat size, /*size in bytes*/
mlsize_t mem, /*resources consumed*/
Expand Down
25 changes: 13 additions & 12 deletions runtime/custom.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ uintnat caml_custom_major_ratio = Custom_major_ratio_def;
uintnat caml_custom_minor_ratio = Custom_minor_ratio_def;
uintnat caml_custom_minor_max_bsz = Custom_minor_max_bsz_def;

/* [mem] is an amount of out-of-heap resources, in the same units as
[max_major] and [max_minor]. When the cumulated amount of such
resources reaches [max_minor] (for resources held by the minor
heap) we do a minor collection; when it reaches [max_major] (for
resources held by the major heap), we guarantee that a major cycle
is done.
If [max_major] is 0, then [mem] is a number of bytes and the actual
limit is [heap_size / 150 * caml_custom_major_ratio], computed at the
time when the custom block is promoted to the major heap.
*/
static value alloc_custom_gen (const struct custom_operations * ops,
uintnat bsz,
mlsize_t mem,
Expand Down Expand Up @@ -75,27 +86,17 @@ CAMLexport value caml_alloc_custom(const struct custom_operations * ops,
mlsize_t mem,
mlsize_t max)
{
if (max == 0) max = 1;
return alloc_custom_gen (ops, bsz, mem, max, max);
}

CAMLexport value caml_alloc_custom_mem(const struct custom_operations * ops,
uintnat bsz,
mlsize_t mem)
{
mlsize_t max_major =
/* The major ratio is a percentage relative to the major heap size.
A complete GC cycle will be done every time 2/3 of that much memory
is allocated for blocks in the major heap. Assuming constant
allocation and deallocation rates, this means there are at most
[M/100 * major-heap-size] bytes of floating garbage at any time.
The reason for a factor of 2/3 (or 1.5) is, roughly speaking, because
the major GC takes 1.5 cycles (previous cycle + marking phase) before
it starts to deallocate dead blocks allocated during the previous cycle.
[heap_size / 150] is really [heap_size * (2/3) / 100] (but faster). */
caml_heap_size(Caml_state->shared_heap) / 150 * caml_custom_major_ratio;
mlsize_t max_minor =
Bsize_wsize (Caml_state->minor_heap_wsz) / 100 * caml_custom_minor_ratio;
value v = alloc_custom_gen (ops, bsz, mem, max_major, max_minor);
value v = alloc_custom_gen (ops, bsz, mem, 0, max_minor);
return v;
}

Expand Down
1 change: 0 additions & 1 deletion runtime/gc_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ extern uintnat caml_major_heap_increment; /* percent or words; see major_gc.c */
extern uintnat caml_percent_free; /* see major_gc.c */
extern uintnat caml_percent_max; /* see compact.c */
extern uintnat caml_allocation_policy; /* see freelist.c */
extern uintnat caml_custom_major_ratio; /* see custom.c */
extern uintnat caml_custom_minor_ratio; /* see custom.c */
extern uintnat caml_custom_minor_max_bsz; /* see custom.c */
extern uintnat caml_minor_heap_max_wsz; /* see domain.c */
Expand Down
1 change: 1 addition & 0 deletions runtime/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <stdarg.h>
#include <stddef.h>
#include "caml/config.h"
#include "caml/custom.h"
#include "caml/misc.h"
#include "caml/fail.h"
#include "caml/memory.h"
Expand Down
18 changes: 18 additions & 0 deletions runtime/minor_gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,24 @@ static void custom_finalize_minor (caml_domain_state * domain)
value *v = &elt->block;
if (Is_block(*v) && Is_young(*v)) {
if (get_header_val(*v) == 0) { /* value copied to major heap */
if (elt->max == 0){
/* When [max] is proportional to heap size, use the current heap
size, not the (major) heap size at the time of allocation in
the minor heap. */
/* The major ratio is a percentage relative to the major heap
size. A complete GC cycle will be done every time 2/3 of
that much memory is allocated for blocks in the major heap.
Assuming constant allocation and deallocation rates, this
means there are at most [M/100 * major-heap-size] bytes of
floating garbage at any time. The reason for a factor of
2/3 (or 1.5) is, roughly speaking, because the major GC
takes 1.5 cycles (previous cycle + marking phase) before it
starts to deallocate dead blocks allocated during the
previous cycle. [heap_size / 150] is really [heap_size *
(2/3) / 100] (but faster). */
elt->max = caml_heap_size(Caml_state->shared_heap) / 150
* caml_custom_major_ratio;
}
caml_adjust_gc_speed(elt->mem, elt->max);
} else {
void (*final_fun)(value) = Custom_ops_val(*v)->finalize;
Expand Down

0 comments on commit a7cd0c6

Please sign in to comment.