Skip to content

Commit

Permalink
Make heap size hint available as an env variable (#55631)
Browse files Browse the repository at this point in the history
This makes `JULIA_HEAP_SIZE_HINT` the environment variable version of
the `--heap-size-hint` command-line flag. Seems like there was interest
in
#45369 (comment).

The same syntax is used as for the command-line version with, for
example, `2G` => 2 GB and `200M` => 200 MB.

@oscardssmith want to take a look?
  • Loading branch information
MilesCranmer authored Nov 7, 2024
1 parent 8593792 commit f336314
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 48 deletions.
17 changes: 17 additions & 0 deletions doc/src/manual/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,23 @@ 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=<size>[<unit>]` 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)

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
Expand Down
7 changes: 7 additions & 0 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3618,6 +3618,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=\"<size>[<unit>]\"");
}
#ifdef _P64
total_mem = uv_get_total_memory();
if (hint == 0) {
Expand Down
103 changes: 55 additions & 48 deletions src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
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)
Expand Down Expand Up @@ -231,10 +279,11 @@ 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=<size> Forces garbage collection if memory usage is higher\n"
" --heap-size-hint=<size>[<unit>] 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: B, K (kibibytes),\n"
" M (mebibytes), G (gibibytes), T (tebibytes), or % (percentage\n"
" of physical memory).\n\n"
;

static const char opts_hidden[] =
Expand Down Expand Up @@ -880,52 +929,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=<size>[<unit>]");
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=<size>[<unit>]");

break;
case opt_gc_threads:
Expand Down
2 changes: 2 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -2536,6 +2536,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);
Expand Down
3 changes: 3 additions & 0 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f336314

Please sign in to comment.