From c9c62687e3d2c21453a688f860de8bf5a8432b78 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Thu, 29 Aug 2024 20:21:11 +0100 Subject: [PATCH 01/10] refactor: split out heap-size-hint parser --- src/jloptions.c | 96 ++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index 4cdec2c7b367f..4dc6a07acbc4b 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -34,6 +34,54 @@ JL_DLLEXPORT const char *jl_get_default_sysimg_path(void) return &system_image_path[1]; } +/* This function is also used by gc-stock.c to parse the + * JULIA_HEAP_SIZE_HINT environment variable. */ +static uint64_t parse_heap_size_hint(const char *optarg, const char *option_name) +{ + long double value = 0.0; + char unit[4] = {0}; + int nparsed = sscanf(optarg, "%Lf%3s", &value, unit); + if (nparsed == 0 || strlen(unit) > 2 || (strlen(unit) == 2 && ascii_tolower(unit[1]) != 'b')) { + jl_errorf("julia: invalid argument to %s (%s)", option_name, optarg); + } + uint64_t multiplier = 1ull; + switch (ascii_tolower(unit[0])) { + case '\0': + case 'b': + break; + case 'k': + multiplier <<= 10; + break; + case 'm': + multiplier <<= 20; + break; + case 'g': + multiplier <<= 30; + break; + case 't': + multiplier <<= 40; + break; + case '%': + if (value > 100) + jl_errorf("julia: invalid percentage specified in %s", option_name); + uint64_t mem = uv_get_total_memory(); + uint64_t cmem = uv_get_constrained_memory(); + if (cmem > 0 && cmem < mem) + mem = cmem; + multiplier = mem/100; + break; + default: + jl_errorf("julia: invalid argument to %s (%s)", option_name, optarg); + break; + } + long double sz = value * multiplier; + if (isnan(sz) || sz < 0) { + jl_errorf("julia: invalid argument to %s (%s)", option_name, optarg); + } + const long double limit = ldexpl(1.0, 64); // UINT64_MAX + 1 + return sz < limit ? (uint64_t)sz : UINT64_MAX; +} + static int jl_options_initialized = 0; JL_DLLEXPORT void jl_init_options(void) @@ -860,52 +908,10 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_options.strip_ir = 1; break; case opt_heap_size_hint: - if (optarg != NULL) { - long double value = 0.0; - char unit[4] = {0}; - int nparsed = sscanf(optarg, "%Lf%3s", &value, unit); - if (nparsed == 0 || strlen(unit) > 2 || (strlen(unit) == 2 && ascii_tolower(unit[1]) != 'b')) { - jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg); - } - uint64_t multiplier = 1ull; - switch (ascii_tolower(unit[0])) { - case '\0': - case 'b': - break; - case 'k': - multiplier <<= 10; - break; - case 'm': - multiplier <<= 20; - break; - case 'g': - multiplier <<= 30; - break; - case 't': - multiplier <<= 40; - break; - case '%': - if (value > 100) - jl_errorf("julia: invalid percentage specified in --heap-size-hint"); - uint64_t mem = uv_get_total_memory(); - uint64_t cmem = uv_get_constrained_memory(); - if (cmem > 0 && cmem < mem) - mem = cmem; - multiplier = mem/100; - break; - default: - jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg); - break; - } - long double sz = value * multiplier; - if (isnan(sz) || sz < 0) { - jl_errorf("julia: invalid argument to --heap-size-hint (%s)", optarg); - } - const long double limit = ldexpl(1.0, 64); // UINT64_MAX + 1 - jl_options.heap_size_hint = sz < limit ? (uint64_t)sz : UINT64_MAX; - } + if (optarg != NULL) + jl_options.heap_size_hint = parse_heap_size_hint(optarg, "--heap-size-hint=[]"); if (jl_options.heap_size_hint == 0) - jl_errorf("julia: invalid memory size specified in --heap-size-hint"); + jl_errorf("julia: invalid memory size specified in --heap-size-hint=[]"); break; case opt_gc_threads: From fc3c24296d2af8df4962cb6ee82e6cc05982ba72 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Thu, 29 Aug 2024 20:22:09 +0100 Subject: [PATCH 02/10] docs: improve docstring for `heap-size-hint` --- src/jloptions.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index 4dc6a07acbc4b..ef70983515fe1 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -277,10 +277,15 @@ static const char opts[] = " current environment and fallbacks to the latest\n" " compatible BugReporting.jl if not. For more\n" " information, see --bug-report=help.\n\n" - " --heap-size-hint= Forces garbage collection if memory usage is higher\n" + " --heap-size-hint=[] Forces garbage collection if memory usage is higher\n" " than the given value. The value may be specified as a\n" - " number of bytes, optionally in units of KB, MB, GB,\n" - " or TB, or as a percentage of physical memory with %.\n\n" + " number of bytes, optionally in units of:\n" + " - b (bytes)\n" + " - k (kibibytes)\n" + " - m (mebibytes)\n" + " - g (gibibytes)\n" + " - t (tebibytes)\n" + " - % (percentage of physical memory)\n\n" ; static const char opts_hidden[] = From 8d552cff75b97f21ec7e10281431f4fe2a0a5f98 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Thu, 29 Aug 2024 20:24:16 +0100 Subject: [PATCH 03/10] feat: add env variable for heap size hint --- src/gc-stock.c | 7 +++++++ src/options.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/gc-stock.c b/src/gc-stock.c index d25f8917f302d..d61d9387fe143 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -3575,6 +3575,13 @@ void jl_gc_init(void) uint64_t mem_reserve = 250*1024*1024; // LLVM + other libraries need some amount of memory uint64_t min_heap_size_hint = mem_reserve + 1*1024*1024; uint64_t hint = jl_options.heap_size_hint; + + // check if heap size specified on command line + if (jl_options.heap_size_hint == 0) { + char *cp = getenv(HEAP_SIZE_HINT); + if (cp) + hint = parse_heap_size_hint(cp, "JULIA_HEAP_SIZE_HINT=\"[]\""); + } #ifdef _P64 total_mem = uv_get_total_memory(); if (hint == 0) { diff --git a/src/options.h b/src/options.h index 800be866183b0..0715069faab32 100644 --- a/src/options.h +++ b/src/options.h @@ -137,6 +137,9 @@ // GC threads #define NUM_GC_THREADS_NAME "JULIA_NUM_GC_THREADS" +// heap size hint +#define HEAP_SIZE_HINT "JULIA_HEAP_SIZE_HINT" + // affinitization behavior #define MACHINE_EXCLUSIVE_NAME "JULIA_EXCLUSIVE" #define DEFAULT_MACHINE_EXCLUSIVE 0 From de9c51db2a89b92908b7f5ca6e6df3629167432f Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Thu, 29 Aug 2024 20:29:37 +0100 Subject: [PATCH 04/10] fix: make heap size hint parsing available --- src/gc-stock.c | 1 + src/jloptions.c | 2 +- src/jloptions.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gc-stock.c b/src/gc-stock.c index d61d9387fe143..75a30036b1d5e 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -9,6 +9,7 @@ #include "julia_atomics.h" #include "julia_gcext.h" #include "julia_assert.h" +#include "jloptions.h" #ifdef __GLIBC__ #include // for malloc_trim #endif diff --git a/src/jloptions.c b/src/jloptions.c index ef70983515fe1..ebdd9af9ff824 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -36,7 +36,7 @@ JL_DLLEXPORT const char *jl_get_default_sysimg_path(void) /* This function is also used by gc-stock.c to parse the * JULIA_HEAP_SIZE_HINT environment variable. */ -static uint64_t parse_heap_size_hint(const char *optarg, const char *option_name) +uint64_t parse_heap_size_hint(const char *optarg, const char *option_name) { long double value = 0.0; char unit[4] = {0}; diff --git a/src/jloptions.h b/src/jloptions.h index aac2a64a373a8..66667a100c23d 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -64,4 +64,6 @@ typedef struct { int8_t trace_compile_timing; } jl_options_t; +uint64_t parse_heap_size_hint(const char *optarg, const char *option_name); + #endif From 7d76e7fe1d71d9426defef0330433ae3ca7ca82a Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Thu, 29 Aug 2024 20:36:36 +0100 Subject: [PATCH 05/10] refactor: move `parse_heap_size_hint` header to julia.h --- src/gc-stock.c | 1 - src/jloptions.h | 2 -- src/julia.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gc-stock.c b/src/gc-stock.c index 75a30036b1d5e..d61d9387fe143 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -9,7 +9,6 @@ #include "julia_atomics.h" #include "julia_gcext.h" #include "julia_assert.h" -#include "jloptions.h" #ifdef __GLIBC__ #include // for malloc_trim #endif diff --git a/src/jloptions.h b/src/jloptions.h index 66667a100c23d..aac2a64a373a8 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -64,6 +64,4 @@ typedef struct { int8_t trace_compile_timing; } jl_options_t; -uint64_t parse_heap_size_hint(const char *optarg, const char *option_name); - #endif diff --git a/src/julia.h b/src/julia.h index caa938ffeb0d6..f09afc8479564 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2505,6 +2505,8 @@ JL_DLLEXPORT ssize_t jl_sizeof_jl_options(void); JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp); JL_DLLEXPORT char *jl_format_filename(const char *output_pattern); +uint64_t parse_heap_size_hint(const char *optarg, const char *option_name); + // Set julia-level ARGS array according to the arguments provided in // argc/argv JL_DLLEXPORT void jl_set_ARGS(int argc, char **argv); From f277afb908900d003ea7824decc614d1103d2c54 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Sat, 14 Sep 2024 18:49:45 +0100 Subject: [PATCH 06/10] Update environment-variables.md --- doc/src/manual/environment-variables.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index 30f2263904f40..cc9703e947045 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -380,6 +380,21 @@ If set to anything besides `0`, then Julia's thread policy is consistent with running on a dedicated machine: the master thread is on proc 0, and threads are affinitized. Otherwise, Julia lets the operating system handle thread policy. +## Garbage Collection + +### [`JULIA_HEAP_SIZE_HINT`](@id JULIA_HEAP_SIZE_HINT) + +Environment variable equivalent to the `--heap-size-hint` command line option. + +Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of: + + - b (bytes) + - k (kibibytes) + - m (mebibytes) + - g (gibibytes) + - t (tebibytes) + - % (percentage of physical memory) + ## REPL formatting Environment variables that determine how REPL output should be formatted at the From fe0ad662a1041000da1e99775700437a92b0e9c7 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Sat, 14 Sep 2024 18:52:24 +0100 Subject: [PATCH 07/10] Update environment-variables.md --- doc/src/manual/environment-variables.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index cc9703e947045..c14e6d3e6b358 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -395,6 +395,8 @@ Forces garbage collection if memory usage is higher than the given value. The va - t (tebibytes) - % (percentage of physical memory) +which are not case sensitive. For example, `JULIA_HEAP_SIZE_HINT=1G` would provide a 1 GB heap size hint to the garbage collector. + ## REPL formatting Environment variables that determine how REPL output should be formatted at the From 4d51e35a73abe5c627be407faa00d28d37523cef Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Mon, 7 Oct 2024 03:51:15 +0100 Subject: [PATCH 08/10] docs: use 2-char version for heap-size-hint --- src/jloptions.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index ebdd9af9ff824..fd0d5f95d3344 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -279,13 +279,8 @@ static const char opts[] = " information, see --bug-report=help.\n\n" " --heap-size-hint=[] Forces garbage collection if memory usage is higher\n" " than the given value. The value may be specified as a\n" - " number of bytes, optionally in units of:\n" - " - b (bytes)\n" - " - k (kibibytes)\n" - " - m (mebibytes)\n" - " - g (gibibytes)\n" - " - t (tebibytes)\n" - " - % (percentage of physical memory)\n\n" + " number of bytes, optionally in units of: B, KB, MB,\n" + " GB, TB, or as a percentage of physical memory (%)\n\n" ; static const char opts_hidden[] = From a4818dd08a69f01a828670d8c7d62e96404370d7 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Mon, 7 Oct 2024 03:58:43 +0100 Subject: [PATCH 09/10] docs: document single char unit for heap size hint --- doc/src/manual/environment-variables.md | 14 +++++++------- src/jloptions.c | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index abbf9a842f288..211ccfc6c46da 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -396,18 +396,18 @@ affinitized. Otherwise, Julia lets the operating system handle thread policy. ### [`JULIA_HEAP_SIZE_HINT`](@id JULIA_HEAP_SIZE_HINT) -Environment variable equivalent to the `--heap-size-hint` command line option. +Environment variable equivalent to the `--heap-size-hint=[]` command line option. Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of: - - b (bytes) - - k (kibibytes) - - m (mebibytes) - - g (gibibytes) - - t (tebibytes) + - B (bytes) + - K (kibibytes) + - M (mebibytes) + - G (gibibytes) + - T (tebibytes) - % (percentage of physical memory) -which are not case sensitive. For example, `JULIA_HEAP_SIZE_HINT=1G` would provide a 1 GB heap size hint to the garbage collector. +For example, `JULIA_HEAP_SIZE_HINT=1G` would provide a 1 GB heap size hint to the garbage collector. ## REPL formatting diff --git a/src/jloptions.c b/src/jloptions.c index 71b0368edd1c4..a00b4c6d8cce0 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -281,8 +281,9 @@ static const char opts[] = " information, see --bug-report=help.\n\n" " --heap-size-hint=[] Forces garbage collection if memory usage is higher\n" " than the given value. The value may be specified as a\n" - " number of bytes, optionally in units of: B, KB, MB,\n" - " GB, TB, or as a percentage of physical memory (%)\n\n" + " number of bytes, optionally in units of: B, K (kilobytes),\n" + " M (megabytes), G (gigabytes), T (terabytes), or % (percentage\n" + " of physical memory).\n\n" ; static const char opts_hidden[] = From e35f2de440960ca60ff3e994958b034c124977a6 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Mon, 7 Oct 2024 04:01:10 +0100 Subject: [PATCH 10/10] tweak docstring --- src/jloptions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index a00b4c6d8cce0..907f47d9030e4 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -281,8 +281,8 @@ static const char opts[] = " information, see --bug-report=help.\n\n" " --heap-size-hint=[] Forces garbage collection if memory usage is higher\n" " than the given value. The value may be specified as a\n" - " number of bytes, optionally in units of: B, K (kilobytes),\n" - " M (megabytes), G (gigabytes), T (terabytes), or % (percentage\n" + " number of bytes, optionally in units of: B, K (kibibytes),\n" + " M (mebibytes), G (gibibytes), T (tebibytes), or % (percentage\n" " of physical memory).\n\n" ;