From a673404950ed5ba3b5ac9ed357a0426048bed317 Mon Sep 17 00:00:00 2001 From: Kate Date: Sat, 22 Aug 2026 16:12:43 +0100 Subject: [PATCH 1/2] Make the GC work harder on low-end hardware instead of calling Gc.compact --- src/client/opamCliMain.ml | 10 ++++++++++ src/core/opamCommonStubs.c | 31 +++++++++++++++++++++++++++++++ src/core/opamParallel.ml | 15 --------------- src/core/opamStubsTypes.ml | 4 ++++ 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/client/opamCliMain.ml b/src/client/opamCliMain.ml index 4cae7e38100..f921761f658 100644 --- a/src/client/opamCliMain.ml +++ b/src/client/opamCliMain.ml @@ -13,6 +13,8 @@ open OpamCmdliner open OpamStateTypes open OpamTypesBase +let log fmt = OpamConsole.log "MAIN" fmt + exception InvalidCLI of OpamCLIVersion.Sourced.t (* [InvalidFlagContent (flag_name, Some (invalid_value, expected_value))] *) @@ -437,7 +439,15 @@ let rec main_catch_all f = in exit exit_code +let set_gc_params total_ram = + let two_GB = 2048L in + if total_ram > 0L && (total_ram : int64) < two_GB then begin + log "Low end machine detected. Setting the GC to work harder"; + Gc.set {(Gc.get ()) with space_overhead = 20}; + end + let run () = + set_gc_params (OpamStubs.total_ram ()); Stdlib.Option.iter OpamVersion.set_git OpamGitVersion.version; OpamSystem.init (); OpamArg.preinit_opam_env_variables (); diff --git a/src/core/opamCommonStubs.c b/src/core/opamCommonStubs.c index 53f8d355a4a..3550b04e9fa 100644 --- a/src/core/opamCommonStubs.c +++ b/src/core/opamCommonStubs.c @@ -23,9 +23,11 @@ #include #include +#include #else +#include #include #include @@ -71,6 +73,35 @@ CAMLprim value opam_nproc(value _unit) { #endif } +CAMLprim value opam_total_ram(value _unit) { + int64_t total_mem = 0; +#ifdef _WIN32 + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + if (GlobalMemoryStatusEx(&statex) == 0) + return caml_copy_int64(0); + total_mem = statex.ullTotalPhys; +#else + struct rlimit rlim; + if (getrlimit(RLIMIT_AS, &rlim) == 0) { + if (rlim.rlim_max != RLIM_INFINITY) + total_mem = rlim.rlim_max; + if (rlim.rlim_cur != RLIM_INFINITY) + total_mem = rlim.rlim_cur; + } + if (total_mem == 0) { + long pagesize = sysconf(_SC_PAGESIZE); + long phys_pages = sysconf(_SC_PHYS_PAGES); + if (pagesize == -1 || phys_pages == -1) + return caml_copy_int64(0); + total_mem = (int64_t)phys_pages * (int64_t)pagesize; + } +#endif + int64_t one_KB = 1024; + int64_t one_MB = one_KB * one_KB; + return caml_copy_int64(total_mem / one_MB); +} + /* This is done here as it simplifies the dune file */ #ifdef _WIN32 #include "opamInject.c" diff --git a/src/core/opamParallel.ml b/src/core/opamParallel.ml index 4754092642a..e71aa927af2 100644 --- a/src/core/opamParallel.ml +++ b/src/core/opamParallel.ml @@ -58,16 +58,6 @@ module type SIG = sig exception Cyclic of G.V.t list list end -let gc_compact () = - let get_heap () = - let {Gc.heap_words; _} = Gc.quick_stat () in - heap_words * Sys.word_size / 8 / 1024 / 1024 - in - let before = get_heap () in - Gc.compact (); - let after = get_heap () in - log "GC compact (heap %d MB -> %d MB)" before after - module Make (G : G) = struct module G = G @@ -102,8 +92,6 @@ module Make (G : G) = struct default :: defined in - let gc_compacted = ref false in - if G.has_cycle g then ( let sccs = G.scc_list g in let sccs = List.filter (function _::_::_ -> true | _ -> false) sccs in @@ -283,9 +271,6 @@ module Make (G : G) = struct run_seq_command nslots ready n cmd else ( (* Wait for a process to end *) - if not !gc_compacted then - (gc_compact (); - gc_compacted := true); let processes = M.fold (fun n (p,x,_) acc -> (p,(n,x)) :: acc) running [] in diff --git a/src/core/opamStubsTypes.ml b/src/core/opamStubsTypes.ml index e938cd102f6..d40e89293e6 100644 --- a/src/core/opamStubsTypes.ml +++ b/src/core/opamStubsTypes.ml @@ -128,3 +128,7 @@ external is_executable : string -> bool = "opam_is_executable" external nproc : unit -> nativeint = "opam_nproc" (** Returns the number of logical processors of the current machine. Any value below [1] is an error. *) + +external total_ram : unit -> int64 = "opam_total_ram" +(** Returns the total amount of RAM of the current system in MB. + Returns [0L] if that information couldn't be acquired. *) From c46e936b4df0fce006f6ab3dad0c438b1b8bc10d Mon Sep 17 00:00:00 2001 From: Kate Date: Tue, 25 Aug 2026 21:54:20 +0100 Subject: [PATCH 2/2] add cgroup support --- src/core/opamCommonStubs.c | 57 ++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/core/opamCommonStubs.c b/src/core/opamCommonStubs.c index 3550b04e9fa..cf896f650e6 100644 --- a/src/core/opamCommonStubs.c +++ b/src/core/opamCommonStubs.c @@ -25,6 +25,11 @@ #include #include +#ifdef __linux__ +#include +#include +#endif + #else #include @@ -82,20 +87,54 @@ CAMLprim value opam_total_ram(value _unit) { return caml_copy_int64(0); total_mem = statex.ullTotalPhys; #else +#ifdef __linux__ + int cgroup = open("/proc/self/cgroup", O_RDONLY); + if (cgroup != -1) { + char buf[4096]; + char prefix[] = "/sys/fs/cgroup/"; + char suffix[] = "/memory.max"; + size_t sizeof_prefix = sizeof(prefix) - 1; + size_t sizeof_suffix = sizeof(suffix) - 1; + char* buf_mid = &buf[sizeof_prefix]; + size_t buf_mid_len = sizeof(buf) - sizeof_prefix - sizeof_suffix - 1; + ssize_t len = read(cgroup, buf_mid, buf_mid_len); + if (len >= 4 && len < buf_mid_len && + buf_mid[0] == '0' && + buf_mid[1] == ':' && + buf_mid[2] == ':' && + buf_mid[len - 1] == '\n') { + memcpy(buf, prefix, sizeof_prefix); + memmove(buf_mid, &buf_mid[3], len - 3); + memcpy(&buf[sizeof_prefix + len - 4], suffix, sizeof(suffix)); + int cgroup_mem_max = open(buf, O_RDONLY); + if (cgroup_mem_max != -1) { + ssize_t len_mem_max = read(cgroup_mem_max, buf, sizeof(buf) - 1); + if (len_mem_max >= 2 && len_mem_max < sizeof(buf) - 1 && + buf[len_mem_max - 1] == '\n') { + buf[len_mem_max - 1] = '\0'; + total_mem = atoll(buf); + } + close(cgroup_mem_max); + } + } + close(cgroup); + } +#endif + int64_t tmp = total_mem; struct rlimit rlim; if (getrlimit(RLIMIT_AS, &rlim) == 0) { if (rlim.rlim_max != RLIM_INFINITY) - total_mem = rlim.rlim_max; + tmp = rlim.rlim_max; if (rlim.rlim_cur != RLIM_INFINITY) - total_mem = rlim.rlim_cur; - } - if (total_mem == 0) { - long pagesize = sysconf(_SC_PAGESIZE); - long phys_pages = sysconf(_SC_PHYS_PAGES); - if (pagesize == -1 || phys_pages == -1) - return caml_copy_int64(0); - total_mem = (int64_t)phys_pages * (int64_t)pagesize; + tmp = rlim.rlim_cur; + total_mem = (total_mem < tmp) ? total_mem : tmp; } + long pagesize = sysconf(_SC_PAGESIZE); + long phys_pages = sysconf(_SC_PHYS_PAGES); + if (pagesize == -1 || phys_pages == -1) + return caml_copy_int64(0); + tmp = (int64_t)phys_pages * (int64_t)pagesize; + total_mem = (total_mem < tmp) ? total_mem : tmp; #endif int64_t one_KB = 1024; int64_t one_MB = one_KB * one_KB;