1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2024-09-19 10:50:24 -04:00

libstore: add load-limit setting to control parallelism

Closes: #7091
Closes: #6855
Closes: #8105
Co-authored-by: Alex Wied <centromere@users.noreply.github.com>
This commit is contained in:
Emily 2024-07-18 12:47:36 +01:00
parent c4213f0e6c
commit 34f2477d2e
10 changed files with 93 additions and 1 deletions

View file

@ -0,0 +1,4 @@
# Release X.Y (202?-??-??)
- Add a `load-limit` setting to control builder parallelism. This has
also been backported to the 2.18 and later release branches.

View file

@ -199,6 +199,7 @@ struct ClientSettings
time_t maxSilentTime; time_t maxSilentTime;
bool verboseBuild; bool verboseBuild;
unsigned int buildCores; unsigned int buildCores;
std::optional<unsigned int> loadLimit;
bool useSubstitutes; bool useSubstitutes;
StringMap overrides; StringMap overrides;
@ -212,6 +213,7 @@ struct ClientSettings
settings.maxSilentTime = maxSilentTime; settings.maxSilentTime = maxSilentTime;
settings.verboseBuild = verboseBuild; settings.verboseBuild = verboseBuild;
settings.buildCores = buildCores; settings.buildCores = buildCores;
settings.loadLimit.assign(loadLimit);
settings.useSubstitutes = useSubstitutes; settings.useSubstitutes = useSubstitutes;
for (auto & i : overrides) { for (auto & i : overrides) {

View file

@ -166,7 +166,6 @@ public:
R"( R"(
Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/language/derivations.md#builder-execution) of a derivation. Sets the value of the `NIX_BUILD_CORES` environment variable in the [invocation of the `builder` executable](@docroot@/language/derivations.md#builder-execution) of a derivation.
The `builder` executable can use this variable to control its own maximum amount of parallelism. The `builder` executable can use this variable to control its own maximum amount of parallelism.
<!-- <!--
FIXME(@fricklerhandwerk): I don't think this should even be mentioned here. FIXME(@fricklerhandwerk): I don't think this should even be mentioned here.
A very generic example using `derivation` and `xargs` may be more appropriate to explain the mechanism. A very generic example using `derivation` and `xargs` may be more appropriate to explain the mechanism.
@ -176,6 +175,8 @@ public:
The value `0` means that the `builder` should use all available CPU cores in the system. The value `0` means that the `builder` should use all available CPU cores in the system.
The [`load-limit`](#conf-load-limit) setting can be used to limit the total amount of build parallelism based on system load average.
> **Note** > **Note**
> >
> The number of parallel local Nix build jobs is independently controlled with the [`max-jobs`](#conf-max-jobs) setting. > The number of parallel local Nix build jobs is independently controlled with the [`max-jobs`](#conf-max-jobs) setting.
@ -184,6 +185,33 @@ public:
// Don't document the machine-specific default value // Don't document the machine-specific default value
false}; false};
Setting<std::optional<unsigned int>> loadLimit{
this,
{ getDefaultCores() },
"load-limit",
R"(
Sets the value of the `NIX_LOAD_LIMIT` environment variable in the
invocation of builders. Builders can use this value at their discretion
to dynamically control the amount of parallelism with respect to the
machine's load average.
For instance, a builder could use the value to set the `-l` flag to GNU
Make. In this case, if the load average of the machine exceeds
`NIX_LOAD_LIMIT`, the amount of parallelism will be dynamically
reduced.
By default, it is set to the number of cores on the machine.
On busy machines where Nix co-exists with other workloads, or where
build throughput is paramount and memory usage is not a bottleneck, the
default value may not work as intended. In this case, `load-limit`
should be set to a higher value, or to `none` to prevent the
`NIX_LOAD_LIMIT` variable being set at all.
)",
{},
// Don't document the machine-specific default value
false};
/** /**
* Read-only mode. Don't copy stuff to the store, don't change * Read-only mode. Don't copy stuff to the store, don't change
* the database. * the database.

View file

@ -1156,6 +1156,10 @@ void LocalDerivationGoal::initEnv()
/* The maximum number of cores to utilize for parallel building. */ /* The maximum number of cores to utilize for parallel building. */
env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores); env["NIX_BUILD_CORES"] = fmt("%d", settings.buildCores);
/* Provide a load average limit for build tools to throttle jobs. */
if (settings.loadLimit.get())
env["NIX_LOAD_LIMIT"] = fmt("%d", settings.loadLimit.get().value());
initTmpDir(); initTmpDir();
/* Compatibility hack with Nix <= 0.7: if this is a fixed-output /* Compatibility hack with Nix <= 0.7: if this is a fixed-output

View file

@ -105,6 +105,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
DECLARE_CONFIG_SERIALISER(std::string) DECLARE_CONFIG_SERIALISER(std::string)
DECLARE_CONFIG_SERIALISER(std::optional<std::string>) DECLARE_CONFIG_SERIALISER(std::optional<std::string>)
DECLARE_CONFIG_SERIALISER(std::optional<unsigned int>)
DECLARE_CONFIG_SERIALISER(bool) DECLARE_CONFIG_SERIALISER(bool)
DECLARE_CONFIG_SERIALISER(Strings) DECLARE_CONFIG_SERIALISER(Strings)
DECLARE_CONFIG_SERIALISER(StringSet) DECLARE_CONFIG_SERIALISER(StringSet)

View file

@ -273,6 +273,25 @@ template<> std::string BaseSetting<std::optional<std::string>>::to_string() cons
return value ? *value : ""; return value ? *value : "";
} }
template<> std::optional<unsigned int> BaseSetting<std::optional<unsigned int>>::parse(const std::string & str) const
{
if (str == "none") return std::nullopt;
else {
if (auto n = string2Int<unsigned int>(str))
return { *n };
else
throw UsageError("configuration setting '%s' should be 'none' or an integer", name);
}
}
template<> std::string BaseSetting<std::optional<unsigned int>>::to_string() const
{
if (value)
return std::to_string(value.value());
else
return "none";
}
template<> bool BaseSetting<bool>::parse(const std::string & str) const template<> bool BaseSetting<bool>::parse(const std::string & str) const
{ {
if (str == "true" || str == "yes" || str == "1") if (str == "true" || str == "yes" || str == "1")

View file

@ -541,6 +541,8 @@ static void main_nix_build(int argc, char * * argv)
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp;
env["NIX_STORE"] = store->storeDir; env["NIX_STORE"] = store->storeDir;
env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);
if (settings.loadLimit.get())
env["NIX_LOAD_LIMIT"] = std::to_string(settings.loadLimit.get().value());
auto passAsFile = tokenizeString<StringSet>(getOr(drv.env, "passAsFile", "")); auto passAsFile = tokenizeString<StringSet>(getOr(drv.env, "passAsFile", ""));

View file

@ -0,0 +1,8 @@
with import ./config.nix;
mkDerivation {
name = "load-limit";
buildCommand = ''
printf '%s' "''${NIX_LOAD_LIMIT-unset}" > "$out"
'';
}

View file

@ -0,0 +1,23 @@
#!/usr/bin/env bash
source common.sh
TODO_NixOS
clearStore
outPath=$(nix-build --load-limit 100 load-limit.nix --no-out-link)
text=$(cat "$outPath")
if test "$text" != "100"; then exit 1; fi
clearStore
outPath=$(nix-build --load-limit 0 load-limit.nix --no-out-link)
text=$(cat "$outPath")
if test "$text" != "0"; then exit 1; fi
clearStore
outPath=$(nix-build --load-limit none load-limit.nix --no-out-link)
text=$(cat "$outPath")
if test "$text" != "unset"; then exit 1; fi

View file

@ -114,6 +114,7 @@ nix_tests = \
ssh-relay.sh \ ssh-relay.sh \
build.sh \ build.sh \
build-delete.sh \ build-delete.sh \
load-limit.sh \
output-normalization.sh \ output-normalization.sh \
selfref-gc.sh \ selfref-gc.sh \
db-migration.sh \ db-migration.sh \