mirror of
https://github.com/NixOS/nix.git
synced 2025-12-05 00:21:01 +01:00
Merge pull request #14540 from lovesegfault/pre-compute-outputgraph
perf(libstore/derivation-builder): pre-compute outputGraph for linear complexity
This commit is contained in:
commit
01dbbc926f
4 changed files with 57 additions and 41 deletions
|
|
@ -989,10 +989,10 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
error if a cycle is detected and roll back the
|
error if a cycle is detected and roll back the
|
||||||
transaction. Cycles can only occur when a derivation
|
transaction. Cycles can only occur when a derivation
|
||||||
has multiple outputs. */
|
has multiple outputs. */
|
||||||
auto topoSortResult = topoSort(paths, {[&](const StorePath & path) {
|
auto topoSortResult = topoSort(paths, [&](const StorePath & path) {
|
||||||
auto i = infos.find(path);
|
auto i = infos.find(path);
|
||||||
return i == infos.end() ? StorePathSet() : i->second.references;
|
return i == infos.end() ? StorePathSet() : i->second.references;
|
||||||
}});
|
});
|
||||||
|
|
||||||
std::visit(
|
std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
|
|
|
||||||
|
|
@ -313,13 +313,13 @@ MissingPaths Store::queryMissing(const std::vector<DerivedPath> & targets)
|
||||||
|
|
||||||
StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||||
{
|
{
|
||||||
auto result = topoSort(paths, {[&](const StorePath & path) {
|
auto result = topoSort(paths, [&](const StorePath & path) {
|
||||||
try {
|
try {
|
||||||
return queryPathInfo(path)->references;
|
return queryPathInfo(path)->references;
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
return StorePathSet();
|
return StorePathSet();
|
||||||
}
|
}
|
||||||
}});
|
});
|
||||||
|
|
||||||
return std::visit(
|
return std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
|
|
|
||||||
|
|
@ -1396,8 +1396,18 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
struct PerhapsNeedToRegister
|
struct PerhapsNeedToRegister
|
||||||
{
|
{
|
||||||
StorePathSet refs;
|
StorePathSet refs;
|
||||||
|
/**
|
||||||
|
* References to other outputs. Built by looking up in
|
||||||
|
* `scratchOutputsInverse`.
|
||||||
|
*/
|
||||||
|
StringSet otherOutputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* inverse map of scratchOutputs for efficient lookup */
|
||||||
|
std::map<StorePath, std::string> scratchOutputsInverse;
|
||||||
|
for (auto & [outputName, path] : scratchOutputs)
|
||||||
|
scratchOutputsInverse.insert_or_assign(path, outputName);
|
||||||
|
|
||||||
std::map<std::string, std::variant<AlreadyRegistered, PerhapsNeedToRegister>> outputReferencesIfUnregistered;
|
std::map<std::string, std::variant<AlreadyRegistered, PerhapsNeedToRegister>> outputReferencesIfUnregistered;
|
||||||
std::map<std::string, struct stat> outputStats;
|
std::map<std::string, struct stat> outputStats;
|
||||||
for (auto & [outputName, _] : drv.outputs) {
|
for (auto & [outputName, _] : drv.outputs) {
|
||||||
|
|
@ -1466,36 +1476,40 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
references = scanForReferences(blank, actualPath, referenceablePaths);
|
references = scanForReferences(blank, actualPath, referenceablePaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
outputReferencesIfUnregistered.insert_or_assign(outputName, PerhapsNeedToRegister{.refs = references});
|
StringSet referencedOutputs;
|
||||||
|
for (auto & r : references)
|
||||||
|
if (auto * o = get(scratchOutputsInverse, r))
|
||||||
|
referencedOutputs.insert(*o);
|
||||||
|
|
||||||
|
outputReferencesIfUnregistered.insert_or_assign(
|
||||||
|
outputName,
|
||||||
|
PerhapsNeedToRegister{
|
||||||
|
.refs = references,
|
||||||
|
.otherOutputs = referencedOutputs,
|
||||||
|
});
|
||||||
outputStats.insert_or_assign(outputName, std::move(st));
|
outputStats.insert_or_assign(outputName, std::move(st));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto topoSortResult = topoSort(outputsToSort, {[&](const std::string & name) {
|
StringSet emptySet;
|
||||||
auto orifu = get(outputReferencesIfUnregistered, name);
|
|
||||||
if (!orifu)
|
auto topoSortResult = topoSort(outputsToSort, [&](const std::string & name) -> const StringSet & {
|
||||||
throw BuildError(
|
auto * orifu = get(outputReferencesIfUnregistered, name);
|
||||||
BuildResult::Failure::OutputRejected,
|
if (!orifu)
|
||||||
"no output reference for '%s' in build of '%s'",
|
throw BuildError(
|
||||||
name,
|
BuildResult::Failure::OutputRejected,
|
||||||
store.printStorePath(drvPath));
|
"no output reference for '%s' in build of '%s'",
|
||||||
return std::visit(
|
name,
|
||||||
overloaded{
|
store.printStorePath(drvPath));
|
||||||
/* Since we'll use the already installed versions of these, we
|
return std::visit(
|
||||||
can treat them as leaves and ignore any references they
|
overloaded{
|
||||||
have. */
|
/* Since we'll use the already installed versions of these, we
|
||||||
[&](const AlreadyRegistered &) { return StringSet{}; },
|
can treat them as leaves and ignore any references they
|
||||||
[&](const PerhapsNeedToRegister & refs) {
|
have. */
|
||||||
StringSet referencedOutputs;
|
[&](const AlreadyRegistered &) -> const StringSet & { return emptySet; },
|
||||||
/* FIXME build inverted map up front so no quadratic waste here */
|
[&](const PerhapsNeedToRegister & refs) -> const StringSet & { return refs.otherOutputs; },
|
||||||
for (auto & r : refs.refs)
|
},
|
||||||
for (auto & [o, p] : scratchOutputs)
|
*orifu);
|
||||||
if (r == p)
|
});
|
||||||
referencedOutputs.insert(o);
|
|
||||||
return referencedOutputs;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
*orifu);
|
|
||||||
}});
|
|
||||||
|
|
||||||
auto sortedOutputNames = std::visit(
|
auto sortedOutputNames = std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "nix/util/error.hh"
|
#include "nix/util/error.hh"
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
@ -16,8 +17,9 @@ struct Cycle
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using TopoSortResult = std::variant<std::vector<T>, Cycle<T>>;
|
using TopoSortResult = std::variant<std::vector<T>, Cycle<T>>;
|
||||||
|
|
||||||
template<typename T, typename Compare>
|
template<typename T, typename Compare, std::invocable<const T &> F>
|
||||||
TopoSortResult<T> topoSort(std::set<T, Compare> items, std::function<std::set<T, Compare>(const T &)> getChildren)
|
requires std::same_as<std::remove_cvref_t<std::invoke_result_t<F, const T &>>, std::set<T, Compare>>
|
||||||
|
TopoSortResult<T> topoSort(std::set<T, Compare> items, F && getChildren)
|
||||||
{
|
{
|
||||||
std::vector<T> sorted;
|
std::vector<T> sorted;
|
||||||
decltype(items) visited, parents;
|
decltype(items) visited, parents;
|
||||||
|
|
@ -34,7 +36,7 @@ TopoSortResult<T> topoSort(std::set<T, Compare> items, std::function<std::set<T,
|
||||||
}
|
}
|
||||||
parents.insert(path);
|
parents.insert(path);
|
||||||
|
|
||||||
auto references = getChildren(path);
|
auto && references = std::invoke(getChildren, path);
|
||||||
|
|
||||||
for (auto & i : references)
|
for (auto & i : references)
|
||||||
/* Don't traverse into items that don't exist in our starting set. */
|
/* Don't traverse into items that don't exist in our starting set. */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue