Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caml_alloc_custom_mem #1738

Merged
merged 10 commits into from Nov 6, 2018
Merged
6 changes: 6 additions & 0 deletions Changes
Expand Up @@ -258,6 +258,12 @@ Working version

### Runtime system:

- MPR#7750, GPR#1738: add a function (caml_custom_alloc_mem) and three
GC parameters to give the user better control of the out-of-heap
memory retained by custom values; use the function to allocate
bigarrays and I/O channels.
(Damien Doligez, review by Alain Frisch)

- GPR#1793: add the -m and -M command-line options to ocamlrun.
(Sébastien Hinderer, review by Xavier Clerc and Damien Doligez)

Expand Down
37 changes: 37 additions & 0 deletions man/ocamlrun.m
Expand Up @@ -125,6 +125,7 @@ in the OCAMLRUNPARAM environment variable (see below).
record documented in
.IR "The OCaml user's manual",
chapter "Standard Library", section "Gc".
\" FIXME missing: c, H, t, w, W see MPR#7870
.TP
.B b
Trigger the printing of a stack backtrace
Expand Down Expand Up @@ -168,6 +169,42 @@ The default size increment for the major heap (in words).
.BR l \ (stack_limit)
The limit (in words) of the stack size.
.TP
.BR M \ (custom_major_ratio)
Target ratio of floating garbage to
major heap size for out-of-heap memory held by custom values
located in the major heap. The GC speed is adjusted
to try to use this much memory for dead values that are not yet
collected. Expressed as a percentage of major heap size.
The default value keeps the out-of-heap floating garbage about the
same size as the in-heap overhead.
Note: this only applies to values allocated with
.B caml_alloc_custom_mem
(e.g. bigarrays).
Default: 44.
.TP
.BR m \ (custom_minor_ratio)
Bound on floating garbage for out-of-heap memory
held by custom values in the minor heap. A minor GC is triggered
when this much memory is held by custom values located in the minor
heap. Expressed as a percentage of minor heap size.
Note: this only applies to values allocated with
.B caml_alloc_custom_mem
(e.g. bigarrays).
Default: 100.
.TP
.BR n \ (custom_minor_max_size)
Maximum amount of out-of-heap
memory for each custom value allocated in the minor heap. When a custom
value is allocated on the minor heap and holds more than this many
bytes, only this value is counted against
.B custom_minor_ratio
and the rest is directly counted against
.BR custom_major_ratio .
Note: this only applies to values allocated with
.B caml_alloc_custom_mem
(e.g. bigarrays).
Default: 8192 bytes.
.TP
.BR v \ (verbose)
What GC messages to print to stderr. This is a sum of values selected
from the following:
Expand Down
15 changes: 14 additions & 1 deletion manual/manual/cmds/intf-c.etex
Expand Up @@ -1864,7 +1864,8 @@ functions, and do not use "CAMLreturn" to return the result.

\subsection{Allocating custom blocks}

Custom blocks must be allocated via the "caml_alloc_custom" function:
Custom blocks must be allocated via "caml_alloc_custom" or
"caml_alloc_custom_mem":
\begin{center}
"caml_alloc_custom("\var{ops}", "\var{size}", "\var{used}", "\var{max}")"
\end{center}
Expand Down Expand Up @@ -1916,6 +1917,18 @@ $\var{used} = 0$ and $\var{max} = 1$. But if you later find that the
finalization functions are not called ``often enough'', consider
increasing the $\var{used} / \var{max}$ ratio.

\begin{center}
"caml_alloc_custom_mem("\var{ops}", "\var{size}", "\var{used}")"
\end{center}
Use this function when your custom block holds only out-of-heap memory
(memory allocated with "malloc" or "caml_stat_alloc") and no other
resources. "used" should be the number of bytes of out-of-heap
memory that are held by your custom block. This function works like
"caml_alloc_custom" except that the "max" parameter is under the
control of the user (via the "custom_major_ratio",
"custom_minor_ratio", and "custom_minor_max_size" parameters) and
proportional to the heap sizes.

\subsection{Accessing custom blocks}

The data part of a custom block \var{v} can be
Expand Down
22 changes: 22 additions & 0 deletions manual/manual/cmds/runtime.etex
Expand Up @@ -157,6 +157,28 @@ The following environment variables are also consulted:
"caml_shutdown" in section~\ref{s:embedded-code}). The option also enables
pooling (as in "caml_startup_pooled"). This mode can be used to detect
leaks with a third-party memory debugger.
% FIXME missing: H, t, w, W see MPR#7870
\item[M] ("custom_major_ratio") Target ratio of floating garbage to
major heap size for out-of-heap memory held by custom values
(e.g. bigarrays) located in the major heap. The GC speed is adjusted
to try to use this much memory for dead values that are not yet
collected. Expressed as a percentage of major heap size. Default:
44. Note: this only applies to values allocated with
"caml_alloc_custom_mem".
\item[m] ("custom_minor_ratio") Bound on floating garbage for
out-of-heap memory
held by custom values in the minor heap. A minor GC is triggered
when this much memory is held by custom values located in the minor
heap. Expressed as a percentage of minor heap size. Default:
100. Note: this only applies to values allocated with
"caml_alloc_custom_mem".
\item[n] ("custom_minor_max_size") Maximum amount of out-of-heap
memory for each custom value allocated in the minor heap. When a custom
value is allocated on the minor heap and holds more than this many
bytes, only this value is counted against "custom_minor_ratio" and
the rest is directly counted against "custom_major_ratio".
Default: 8192 bytes. Note:
this only applies to values allocated with "caml_alloc_custom_mem".
damiendoligez marked this conversation as resolved.
Show resolved Hide resolved
\end{options}
The multiplier is "k", "M", or "G", for multiplication by $2^{10}$,
$2^{20}$, and $2^{30}$ respectively.
Expand Down
6 changes: 1 addition & 5 deletions runtime/bigarray.c
Expand Up @@ -79,10 +79,6 @@ CAMLexport struct custom_operations caml_ba_ops = {

/* Allocation of a big array */

#define CAML_BA_MAX_MEMORY (1024*1024*1024)
/* 1 Gb -- after allocating that much, it's probably worth speeding
up the major GC */

/* [caml_ba_alloc] will allocate a new bigarray object in the heap.
If [data] is NULL, the memory for the contents is also allocated
(with [malloc]) by [caml_ba_alloc].
Expand Down Expand Up @@ -117,7 +113,7 @@ caml_ba_alloc(int flags, int num_dims, void * data, intnat * dim)
flags |= CAML_BA_MANAGED;
}
asize = SIZEOF_BA_ARRAY + num_dims * sizeof(intnat);
res = caml_alloc_custom(&caml_ba_ops, asize, size, CAML_BA_MAX_MEMORY);
res = caml_alloc_custom_mem(&caml_ba_ops, asize, size);
b = Caml_ba_array_val(res);
b->data = data;
b->num_dims = num_dims;
Expand Down
13 changes: 13 additions & 0 deletions runtime/caml/config.h
Expand Up @@ -222,4 +222,17 @@ typedef uint64_t uintnat;
/* Maximum size of the major GC slice smoothing window. */
#define Max_major_window 50

/* Default setting for the ratio of custom garbage to major heap size.
Documented in gc.mli */
#define Custom_major_ratio_def 44

/* Default setting for the ratio of custom garbage to minor heap size.
Documented in gc.mli */
#define Custom_minor_ratio_def 100

/* Default setting for maximum size of custom objects counted as garbage
in the minor heap.
Documented in gc.mli */
#define Custom_minor_max_bsz_def 8192

#endif /* CAML_CONFIG_H */
4 changes: 4 additions & 0 deletions runtime/caml/custom.h
Expand Up @@ -60,6 +60,10 @@ CAMLextern value caml_alloc_custom(struct custom_operations * ops,
mlsize_t mem, /*resources consumed*/
mlsize_t max /*max resources*/);

CAMLextern value caml_alloc_custom_mem(struct custom_operations * ops,
uintnat size, /*size in bytes*/
mlsize_t mem /*memory consumed*/);

CAMLextern void caml_register_custom_operations(struct custom_operations * ops);

CAMLextern int caml_compare_unordered;
Expand Down
6 changes: 5 additions & 1 deletion runtime/caml/gc_ctrl.h
Expand Up @@ -42,9 +42,13 @@ uintnat caml_normalize_heap_increment (uintnat);
percent_fr: cf. space_overhead in gc.mli
percent_m : cf. max_overhead in gc.mli
window : cf. window_size in gc.mli
custom_maj: cf. custom_major_ratio in gc.mli
custom_min: cf. custom_minor_ratio in gc.mli
custom_sz : cf. custom_minor_max_size in gc.mli
*/
void caml_init_gc (uintnat minor_size, uintnat major_size, uintnat major_incr,
uintnat percent_fr, uintnat percent_m, uintnat window);
uintnat percent_fr, uintnat percent_m, uintnat window,
uintnat custom_maj, uintnat custom_min, uintnat custom_bsz);


CAMLextern value caml_gc_stat(value v);
Expand Down
3 changes: 3 additions & 0 deletions runtime/caml/startup_aux.h
Expand Up @@ -32,6 +32,9 @@ extern uintnat caml_init_heap_chunk_sz;
extern uintnat caml_init_heap_wsz;
extern uintnat caml_init_max_stack_wsz;
extern uintnat caml_init_major_window;
extern uintnat caml_init_custom_major_ratio;
extern uintnat caml_init_custom_minor_ratio;
extern uintnat caml_init_custom_minor_max_bsz;
extern uintnat caml_trace_level;
extern int caml_cleanup_on_exit;

Expand Down
61 changes: 49 additions & 12 deletions runtime/custom.c
Expand Up @@ -20,32 +20,47 @@
#include "caml/alloc.h"
#include "caml/custom.h"
#include "caml/fail.h"
#include "caml/gc_ctrl.h"
#include "caml/memory.h"
#include "caml/mlvalues.h"
#include "caml/signals.h"

/* [size] is a number of bytes */
CAMLexport value caml_alloc_custom(struct custom_operations * ops,
uintnat size,
mlsize_t mem,
mlsize_t max)
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;

static value alloc_custom_gen (struct custom_operations * ops,
uintnat bsz,
mlsize_t mem,
mlsize_t max_major,
mlsize_t mem_minor,
mlsize_t max_minor)
{
mlsize_t wosize;
CAMLparam0();
CAMLlocal1(result);

wosize = 1 + (size + sizeof(value) - 1) / sizeof(value);
/* [mem] is the total amount of out-of-heap memory, [mem_minor] is how much
of it should be counted against [max_minor]. */
CAMLassert (mem_minor <= mem);

wosize = 1 + (bsz + sizeof(value) - 1) / sizeof(value);
if (wosize <= Max_young_wosize) {
result = caml_alloc_small(wosize, Custom_tag);
Custom_ops_val(result) = ops;
if (ops->finalize != NULL || mem != 0) {
/* Remember that the block needs processing after minor GC. */
add_to_custom_table (&caml_custom_table, result, mem, max);
if (mem > mem_minor) {
caml_adjust_gc_speed (mem - mem_minor, max_major);
}
/* The remaining [mem_minor] will be counted if the block survives a
minor GC */
add_to_custom_table (&caml_custom_table, result, mem_minor, max_major);
/* Keep track of extra resources held by custom block in
minor heap. */
if (mem != 0) {
if (max == 0) max = 1;
caml_extra_heap_resources_minor += (double) mem / (double) max;
if (mem_minor != 0) {
if (max_minor == 0) max_minor = 1;
caml_extra_heap_resources_minor +=
(double) mem_minor / (double) max_minor;
if (caml_extra_heap_resources_minor > 1.0) {
caml_request_minor_gc ();
caml_gc_dispatch ();
Expand All @@ -55,12 +70,34 @@ CAMLexport value caml_alloc_custom(struct custom_operations * ops,
} else {
result = caml_alloc_shr(wosize, Custom_tag);
Custom_ops_val(result) = ops;
caml_adjust_gc_speed(mem, max);
caml_adjust_gc_speed(mem, max_major);
result = caml_check_urgent_gc(result);
}
CAMLreturn(result);
}

CAMLexport value caml_alloc_custom(struct custom_operations * ops,
uintnat bsz,
mlsize_t mem,
mlsize_t max)
{
return alloc_custom_gen (ops, bsz, mem, max, mem, max);
}

CAMLexport value caml_alloc_custom_mem(struct custom_operations * ops,
uintnat bsz,
mlsize_t mem)
{
mlsize_t mem_minor =
mem < caml_custom_minor_max_bsz ? mem : caml_custom_minor_max_bsz;
return alloc_custom_gen (ops, bsz, mem,
Bsize_wsize (caml_stat_heap_wsz) / 150
* caml_custom_major_ratio,
mem_minor,
Bsize_wsize (caml_minor_heap_wsz) / 100
* caml_custom_major_ratio);
}

struct custom_operations_list {
struct custom_operations * ops;
struct custom_operations_list * next;
Expand Down
51 changes: 49 additions & 2 deletions runtime/gc_ctrl.c
Expand Up @@ -56,6 +56,9 @@ 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 */

#define Next(hp) ((hp) + Whsize_hp (hp))

Expand Down Expand Up @@ -356,7 +359,7 @@ CAMLprim value caml_gc_get(value v)
CAMLparam0 (); /* v is ignored */
CAMLlocal1 (res);

res = caml_alloc_tuple (8);
res = caml_alloc_tuple (11);
Store_field (res, 0, Val_long (caml_minor_heap_wsz)); /* s */
Store_field (res, 1, Val_long (caml_major_heap_increment)); /* i */
Store_field (res, 2, Val_long (caml_percent_free)); /* o */
Expand All @@ -369,6 +372,9 @@ CAMLprim value caml_gc_get(value v)
#endif
Store_field (res, 6, Val_long (caml_allocation_policy)); /* a */
Store_field (res, 7, Val_long (caml_major_window)); /* w */
Store_field (res, 8, Val_long (caml_custom_major_ratio)); /* M */
Store_field (res, 9, Val_long (caml_custom_minor_ratio)); /* m */
Store_field (res, 10, Val_long (caml_custom_minor_max_bsz)); /* n */
CAMLreturn (res);
}

Expand Down Expand Up @@ -398,12 +404,23 @@ static uintnat norm_window (intnat w)
return w;
}

static uintnat norm_custom_maj (uintnat p)
{
return Max (p, 1);
}

static uintnat norm_custom_min (uintnat p)
{
return Max (p, 1);
}

CAMLprim value caml_gc_set(value v)
{
uintnat newpf, newpm;
asize_t newheapincr;
asize_t newminwsz;
uintnat oldpolicy;
uintnat new_custom_maj, new_custom_min, new_custom_sz;
CAML_INSTR_SETUP (tmr, "");

caml_verb_gc = Long_val (Field (v, 3));
Expand Down Expand Up @@ -456,6 +473,31 @@ CAMLprim value caml_gc_set(value v)
}
}

/* These fields were added in 4.08.0. */
if (Wosize_val (v) >= 11){
new_custom_maj = norm_custom_maj (Field (v, 8));
if (new_custom_maj != caml_custom_major_ratio){
caml_custom_major_ratio = new_custom_maj;
caml_gc_message (0x20, "New custom major ratio: %"
ARCH_INTNAT_PRINTF_FORMAT "u%%\n",
caml_custom_major_ratio);
}
new_custom_min = norm_custom_min (Field (v, 9));
if (new_custom_min != caml_custom_minor_ratio){
caml_custom_minor_ratio = new_custom_min;
caml_gc_message (0x20, "New custom minor ratio: %"
ARCH_INTNAT_PRINTF_FORMAT "u%%\n",
caml_custom_minor_ratio);
}
new_custom_sz = Field (v, 10);
if (new_custom_sz != caml_custom_minor_max_bsz){
caml_custom_minor_max_bsz = new_custom_sz;
caml_gc_message (0x20, "New custom minor size limit: %"
ARCH_INTNAT_PRINTF_FORMAT "u%%\n",
caml_custom_minor_max_bsz);
}
}

/* Minor heap size comes last because it will trigger a minor collection
(thus invalidating [v]) and it can raise [Out_of_memory]. */
newminwsz = norm_minsize (Long_val (Field (v, 0)));
Expand Down Expand Up @@ -584,7 +626,9 @@ uintnat caml_normalize_heap_increment (uintnat i)
[major_incr] is either a percentage or a number of words */
void caml_init_gc (uintnat minor_size, uintnat major_size,
uintnat major_incr, uintnat percent_fr,
uintnat percent_m, uintnat window)
uintnat percent_m, uintnat window,
uintnat custom_maj, uintnat custom_min,
uintnat custom_bsz)
{
uintnat major_heap_size =
Bsize_wsize (caml_normalize_heap_increment (major_size));
Expand All @@ -602,6 +646,9 @@ void caml_init_gc (uintnat minor_size, uintnat major_size,
caml_percent_max = norm_pmax (percent_m);
caml_init_major_heap (major_heap_size);
caml_major_window = norm_window (window);
caml_custom_major_ratio = norm_custom_maj (custom_maj);
caml_custom_minor_ratio = norm_custom_min (custom_min);
caml_custom_minor_max_bsz = custom_bsz;
caml_gc_message (0x20, "Initial minor heap size: %"
ARCH_SIZET_PRINTF_FORMAT "uk words\n",
caml_minor_heap_wsz / 1024);
Expand Down