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:
parent
188cb798ad
commit
723c47550e
7 changed files with 218 additions and 1 deletions
14
src/libexpr-tests/bench-main.cc
Normal file
14
src/libexpr-tests/bench-main.cc
Normal 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;
|
||||
}
|
||||
55
src/libexpr-tests/dynamic-attrs-bench.cc
Normal file
55
src/libexpr-tests/dynamic-attrs-bench.cc
Normal 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);
|
||||
64
src/libexpr-tests/get-drvs-bench.cc
Normal file
64
src/libexpr-tests/get-drvs-bench.cc
Normal 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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
9
src/libexpr-tests/meson.options
Normal file
9
src/libexpr-tests/meson.options
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# vim: filetype=meson
|
||||
|
||||
option(
|
||||
'benchmarks',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'Build benchmarks (requires gbenchmark)',
|
||||
yield : true,
|
||||
)
|
||||
|
|
@ -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") ./.)
|
||||
];
|
||||
|
|
|
|||
45
src/libexpr-tests/regex-cache-bench.cc
Normal file
45
src/libexpr-tests/regex-cache-bench.cc
Normal 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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue