1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-23 17:31:08 +01:00

libexpr-tests: add nix-expr-benchmarks

Provides focused microbenchmarks for expression evaluation hot paths (dynamic attrs, getDerivations attr scanning, and repeated builtins.match).
This commit is contained in:
Kamil Monicz 2025-12-18 03:07:34 +00:00
parent 188cb798ad
commit 723c47550e
No known key found for this signature in database
GPG key ID: F9FB19F1C1DC9C23
7 changed files with 218 additions and 1 deletions

View file

@ -0,0 +1,14 @@
#include <benchmark/benchmark.h>
#include "nix/expr/eval-gc.hh"
#include "nix/store/globals.hh"
int main(int argc, char ** argv)
{
nix::initLibStore(false);
nix::initGC();
::benchmark::Initialize(&argc, argv);
::benchmark::RunSpecifiedBenchmarks();
return 0;
}

View file

@ -0,0 +1,55 @@
#include <benchmark/benchmark.h>
#include "nix/expr/eval.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/fetchers/fetch-settings.hh"
#include "nix/store/store-open.hh"
using namespace nix;
static std::string mkDynamicAttrsExpr(size_t attrCount)
{
std::string res;
res.reserve(attrCount * 24);
res += "{ ";
for (size_t i = 0; i < attrCount; ++i) {
res += "${\"a";
res += std::to_string(i);
res += "\"} = ";
res += std::to_string(i);
res += "; ";
}
res += "}";
return res;
}
static void BM_EvalDynamicAttrs(benchmark::State & state)
{
const auto attrCount = static_cast<size_t>(state.range(0));
const auto exprStr = mkDynamicAttrsExpr(attrCount);
for (auto _ : state) {
state.PauseTiming();
auto store = openStore("dummy://");
fetchers::Settings fetchSettings{};
bool readOnlyMode = true;
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
Expr * expr = st.parseExprFromString(exprStr, st.rootPath(CanonPath::root));
Value v;
state.ResumeTiming();
st.eval(expr, v);
st.forceValue(v, noPos);
benchmark::DoNotOptimize(v);
}
state.SetItemsProcessed(state.iterations() * attrCount);
}
BENCHMARK(BM_EvalDynamicAttrs)->Arg(100)->Arg(500)->Arg(2'000);

View file

@ -0,0 +1,64 @@
#include <benchmark/benchmark.h>
#include "nix/expr/get-drvs.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/fetchers/fetch-settings.hh"
#include "nix/store/store-open.hh"
#include "nix/util/fmt.hh"
using namespace nix;
namespace {
struct GetDerivationsEnv
{
ref<Store> store = openStore("dummy://");
fetchers::Settings fetchSettings{};
bool readOnlyMode = true;
EvalSettings evalSettings{readOnlyMode};
EvalState state;
Bindings * autoArgs = nullptr;
Value attrsValue;
explicit GetDerivationsEnv(size_t attrCount)
: evalSettings([&]() {
EvalSettings settings{readOnlyMode};
settings.nixPath = {};
return settings;
}())
, state({}, store, fetchSettings, evalSettings, nullptr)
{
autoArgs = state.buildBindings(0).finish();
auto attrs = state.buildBindings(attrCount);
for (size_t i = 0; i < attrCount; ++i) {
auto name = fmt("pkg%|1$06d|", i);
auto sym = state.symbols.create(name);
auto & v = attrs.alloc(sym);
v.mkInt(i);
}
attrsValue.mkAttrs(attrs.finish());
}
};
} // namespace
static void BM_GetDerivationsAttrScan(benchmark::State & state)
{
const auto attrCount = static_cast<size_t>(state.range(0));
GetDerivationsEnv env(attrCount);
for (auto _ : state) {
PackageInfos drvs;
getDerivations(
env.state, env.attrsValue, /*pathPrefix=*/"", *env.autoArgs, drvs, /*ignoreAssertionFailures=*/true);
benchmark::DoNotOptimize(drvs.size());
}
state.SetItemsProcessed(state.iterations() * attrCount);
}
BENCHMARK(BM_GetDerivationsAttrScan)->Arg(1'000)->Arg(5'000)->Arg(10'000);

View file

@ -87,3 +87,33 @@ test(
},
protocol : 'gtest',
)
# Build benchmarks if enabled
if get_option('benchmarks')
gbenchmark = dependency('benchmark', required : true)
benchmark_sources = files(
'bench-main.cc',
'dynamic-attrs-bench.cc',
'get-drvs-bench.cc',
'regex-cache-bench.cc',
)
benchmark_exe = executable(
'nix-expr-benchmarks',
benchmark_sources,
config_priv_h,
dependencies : deps_private_subproject + deps_private + deps_other + [
gbenchmark,
],
include_directories : include_dirs,
link_args : linker_export_flags,
install : true,
cpp_pch : do_pch ? [ 'pch/precompiled-headers.hh' ] : [],
)
benchmark(
'nix-expr-benchmarks',
benchmark_exe,
)
endif

View file

@ -0,0 +1,9 @@
# vim: filetype=meson
option(
'benchmarks',
type : 'boolean',
value : false,
description : 'Build benchmarks (requires gbenchmark)',
yield : true,
)

View file

@ -33,7 +33,7 @@ mkMesonExecutable (finalAttrs: {
../../.version
./.version
./meson.build
# ./meson.options
./meson.options
(fileset.fileFilter (file: file.hasExt "cc") ./.)
(fileset.fileFilter (file: file.hasExt "hh") ./.)
];

View file

@ -0,0 +1,45 @@
#include <benchmark/benchmark.h>
#include "nix/expr/eval.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/fetchers/fetch-settings.hh"
#include "nix/store/store-open.hh"
using namespace nix;
static void BM_EvalManyBuiltinsMatchSameRegex(benchmark::State & state)
{
static constexpr int iterations = 5'000;
static constexpr std::string_view exprStr =
"builtins.foldl' "
"(acc: _: acc + builtins.length (builtins.match \"a\" \"a\")) "
"0 "
"(builtins.genList (x: x) "
"5000)";
for (auto _ : state) {
state.PauseTiming();
auto store = openStore("dummy://");
fetchers::Settings fetchSettings{};
bool readOnlyMode = true;
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
Expr * expr = st.parseExprFromString(std::string(exprStr), st.rootPath(CanonPath::root));
Value v;
state.ResumeTiming();
st.eval(expr, v);
st.forceValue(v, noPos);
benchmark::DoNotOptimize(v);
}
state.SetItemsProcessed(state.iterations() * iterations);
}
BENCHMARK(BM_EvalManyBuiltinsMatchSameRegex);