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

Make heap size hint available as an env variable #55631

Merged
merged 15 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/src/manual/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,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` 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)

which are not case sensitive. For example, `JULIA_HEAP_SIZE_HINT=1G` would provide a 1 GB heap size hint to the garbage collector.
MilesCranmer marked this conversation as resolved.
Show resolved Hide resolved

## 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 @@ -3586,6 +3586,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
107 changes: 59 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,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=<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:\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[] =
Expand Down Expand Up @@ -880,52 +933,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 @@ -2515,6 +2515,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