Skip to content

Commit b062730

Browse files
authored
Merge pull request NixOS#13569 from Mic92/benchmark
add derivation parser benchmark
2 parents c85a014 + 1989dd7 commit b062730

File tree

10 files changed

+270
-2
lines changed

10 files changed

+270
-2
lines changed

doc/manual/source/SUMMARY.md.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
- [Development](development/index.md)
129129
- [Building](development/building.md)
130130
- [Testing](development/testing.md)
131+
- [Benchmarking](development/benchmarking.md)
131132
- [Debugging](development/debugging.md)
132133
- [Documentation](development/documentation.md)
133134
- [CLI guideline](development/cli-guideline.md)
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Running Benchmarks
2+
3+
This guide explains how to build and run performance benchmarks in the Nix codebase.
4+
5+
## Overview
6+
7+
Nix uses the [Google Benchmark](https://github.com/google/benchmark) framework for performance testing. Benchmarks help measure and track the performance of critical operations like derivation parsing.
8+
9+
## Building Benchmarks
10+
11+
Benchmarks are disabled by default and must be explicitly enabled during the build configuration. For accurate results, use a debug-optimized release build.
12+
13+
### Development Environment Setup
14+
15+
First, enter the development shell which includes the necessary dependencies:
16+
17+
```bash
18+
nix develop .#native-ccacheStdenv
19+
```
20+
21+
### Configure Build with Benchmarks
22+
23+
From the project root, configure the build with benchmarks enabled and optimization:
24+
25+
```bash
26+
cd build
27+
meson configure -Dbenchmarks=true -Dbuildtype=debugoptimized
28+
```
29+
30+
The `debugoptimized` build type provides:
31+
- Compiler optimizations for realistic performance measurements
32+
- Debug symbols for profiling and analysis
33+
- Balance between performance and debuggability
34+
35+
### Build the Benchmarks
36+
37+
Build the project including benchmarks:
38+
39+
```bash
40+
ninja
41+
```
42+
43+
This will create benchmark executables in the build directory. Currently available:
44+
- `build/src/libstore-tests/nix-store-benchmarks` - Store-related performance benchmarks
45+
46+
Additional benchmark executables will be created as more benchmarks are added to the codebase.
47+
48+
## Running Benchmarks
49+
50+
### Basic Usage
51+
52+
Run benchmark executables directly. For example, to run store benchmarks:
53+
54+
```bash
55+
./build/src/libstore-tests/nix-store-benchmarks
56+
```
57+
58+
As more benchmark executables are added, run them similarly from their respective build directories.
59+
60+
### Filtering Benchmarks
61+
62+
Run specific benchmarks using regex patterns:
63+
64+
```bash
65+
# Run only derivation parser benchmarks
66+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_filter="derivation.*"
67+
68+
# Run only benchmarks for hello.drv
69+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_filter=".*hello.*"
70+
```
71+
72+
### Output Formats
73+
74+
Generate benchmark results in different formats:
75+
76+
```bash
77+
# JSON output
78+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_format=json > results.json
79+
80+
# CSV output
81+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_format=csv > results.csv
82+
```
83+
84+
### Advanced Options
85+
86+
```bash
87+
# Run benchmarks multiple times for better statistics
88+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_repetitions=10
89+
90+
# Set minimum benchmark time (useful for micro-benchmarks)
91+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_min_time=2
92+
93+
# Compare against baseline
94+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_baseline=baseline.json
95+
96+
# Display time in custom units
97+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_time_unit=ms
98+
```
99+
100+
## Writing New Benchmarks
101+
102+
To add new benchmarks:
103+
104+
1. Create a new `.cc` file in the appropriate `*-tests` directory
105+
2. Include the benchmark header:
106+
```cpp
107+
#include <benchmark/benchmark.h>
108+
```
109+
110+
3. Write benchmark functions:
111+
```cpp
112+
static void BM_YourBenchmark(benchmark::State & state)
113+
{
114+
// Setup code here
115+
116+
for (auto _ : state) {
117+
// Code to benchmark
118+
}
119+
}
120+
BENCHMARK(BM_YourBenchmark);
121+
```
122+
123+
4. Add the file to the corresponding `meson.build`:
124+
```meson
125+
benchmarks_sources = files(
126+
'your-benchmark.cc',
127+
# existing benchmarks...
128+
)
129+
```
130+
131+
## Profiling with Benchmarks
132+
133+
For deeper performance analysis, combine benchmarks with profiling tools:
134+
135+
```bash
136+
# Using Linux perf
137+
perf record ./build/src/libstore-tests/nix-store-benchmarks
138+
perf report
139+
```
140+
141+
### Using Valgrind Callgrind
142+
143+
Valgrind's callgrind tool provides detailed profiling information that can be visualized with kcachegrind:
144+
145+
```bash
146+
# Profile with callgrind
147+
valgrind --tool=callgrind ./build/src/libstore-tests/nix-store-benchmarks
148+
149+
# Visualize the results with kcachegrind
150+
kcachegrind callgrind.out.*
151+
```
152+
153+
This provides:
154+
- Function call graphs
155+
- Instruction-level profiling
156+
- Source code annotation
157+
- Interactive visualization of performance bottlenecks
158+
159+
## Continuous Performance Testing
160+
161+
```bash
162+
# Save baseline results
163+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_format=json > baseline.json
164+
165+
# Compare against baseline in CI
166+
./build/src/libstore-tests/nix-store-benchmarks --benchmark_baseline=baseline.json
167+
```
168+
169+
## Troubleshooting
170+
171+
### Benchmarks not building
172+
173+
Ensure benchmarks are enabled:
174+
```bash
175+
meson configure build | grep benchmarks
176+
# Should show: benchmarks true
177+
```
178+
179+
### Inconsistent results
180+
181+
- Ensure your system is not under heavy load
182+
- Disable CPU frequency scaling for consistent results
183+
- Run benchmarks multiple times with `--benchmark_repetitions`
184+
185+
## See Also
186+
187+
- [Google Benchmark documentation](https://github.com/google/benchmark/blob/main/docs/user_guide.md)

meson.options

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,10 @@ option(
2020
value : true,
2121
description : 'Build language bindings (e.g. Perl)',
2222
)
23+
24+
option(
25+
'benchmarks',
26+
type : 'boolean',
27+
value : false,
28+
description : 'Build benchmarks (requires gbenchmark)',
29+
)

packaging/dev-shell.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ pkgs.nixComponents2.nix-util.overrideAttrs (
126126
++ lib.optional stdenv.hostPlatform.isLinux pkgs.buildPackages.mold-wrapped;
127127

128128
buildInputs =
129-
attrs.buildInputs or [ ]
129+
[ pkgs.gbenchmark ]
130+
++ attrs.buildInputs or [ ]
130131
++ pkgs.nixComponents2.nix-util.buildInputs
131132
++ pkgs.nixComponents2.nix-store.buildInputs
132133
++ pkgs.nixComponents2.nix-store-tests.externalBuildInputs

src/libstore-tests/data/derivation/firefox.drv

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Derive([("out","/nix/store/hhg83gh653wjw4ny49xn92f13v2j1za4-hello-2.12.2","","")],[("/nix/store/1xz4avqqrxqsxw7idz119vdzw837p1n1-version-check-hook.drv",["out"]),("/nix/store/bsv47sbqcar3205il55spxqacxp8j0fj-hello-2.12.2.tar.gz.drv",["out"]),("/nix/store/s4b8yadif84kiv8gyr9nxdi6zbg69b4g-bash-5.2p37.drv",["out"]),("/nix/store/sc2pgkzc1s6zp5dp8j7wsd4msilsnijn-stdenv-linux.drv",["out"])],["/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh","/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh"],"x86_64-linux","/nix/store/p79bgyzmmmddi554ckwzbqlavbkw07zh-bash-5.2p37/bin/bash",["-e","/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh","/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"],[("NIX_MAIN_PROGRAM","hello"),("__structuredAttrs",""),("buildInputs",""),("builder","/nix/store/p79bgyzmmmddi554ckwzbqlavbkw07zh-bash-5.2p37/bin/bash"),("cmakeFlags",""),("configureFlags",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck","1"),("doInstallCheck","1"),("mesonFlags",""),("name","hello-2.12.2"),("nativeBuildInputs","/nix/store/fxzn6kr5anxn5jgh511x56wrg8b3a99a-version-check-hook"),("out","/nix/store/hhg83gh653wjw4ny49xn92f13v2j1za4-hello-2.12.2"),("outputs","out"),("patches",""),("pname","hello"),("postInstallCheck","stat \"${!outputBin}/bin/hello\"\n"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("src","/nix/store/dw402azxjrgrzrk6j0p66wkqrab5mwgw-hello-2.12.2.tar.gz"),("stdenv","/nix/store/a13rl87yjhzqrbkc4gb0mrwz2mfkivcf-stdenv-linux"),("strictDeps",""),("system","x86_64-linux"),("version","2.12.2")])
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <benchmark/benchmark.h>
2+
#include "nix/store/derivations.hh"
3+
#include "nix/store/store-api.hh"
4+
#include "nix/util/experimental-features.hh"
5+
#include "nix/store/store-open.hh"
6+
#include "nix/store/globals.hh"
7+
#include <fstream>
8+
#include <sstream>
9+
10+
using namespace nix;
11+
12+
// Benchmark parsing real derivation files
13+
static void BM_ParseRealDerivationFile(benchmark::State & state, const std::string & filename)
14+
{
15+
// Read the file once
16+
std::ifstream file(filename);
17+
std::stringstream buffer;
18+
buffer << file.rdbuf();
19+
std::string content = buffer.str();
20+
21+
auto store = openStore("dummy://");
22+
ExperimentalFeatureSettings xpSettings;
23+
24+
for (auto _ : state) {
25+
auto drv = parseDerivation(*store, std::string(content), "test", xpSettings);
26+
benchmark::DoNotOptimize(drv);
27+
}
28+
state.SetBytesProcessed(state.iterations() * content.size());
29+
}
30+
31+
// Register benchmarks for actual test derivation files if they exist
32+
BENCHMARK_CAPTURE(BM_ParseRealDerivationFile, hello, std::string(NIX_UNIT_TEST_DATA) + "/derivation/hello.drv");
33+
BENCHMARK_CAPTURE(BM_ParseRealDerivationFile, firefox, std::string(NIX_UNIT_TEST_DATA) + "/derivation/firefox.drv");
34+
35+
// Custom main to initialize Nix before running benchmarks
36+
int main(int argc, char ** argv)
37+
{
38+
// Initialize libstore
39+
nix::initLibStore(false);
40+
41+
// Initialize and run benchmarks
42+
::benchmark::Initialize(&argc, argv);
43+
::benchmark::RunSpecifiedBenchmarks();
44+
return 0;
45+
}

src/libstore-tests/meson.build

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,19 @@ test(
105105
},
106106
protocol : 'gtest',
107107
)
108+
109+
# Build benchmarks if enabled
110+
if get_option('benchmarks')
111+
gbenchmark = dependency('benchmark', required : true)
112+
113+
benchmark_exe = executable(
114+
'nix-store-benchmarks',
115+
'derivation-parser-bench.cc',
116+
config_priv_h,
117+
dependencies : deps_private_subproject + deps_private + deps_other + [gbenchmark],
118+
include_directories : include_dirs,
119+
link_args: linker_export_flags,
120+
install : false,
121+
cpp_args : ['-DNIX_UNIT_TEST_DATA="' + meson.current_source_dir() + '/data"'],
122+
)
123+
endif

src/libstore-tests/meson.options

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# vim: filetype=meson
2+
3+
option(
4+
'benchmarks',
5+
type : 'boolean',
6+
value : false,
7+
description : 'Build benchmarks (requires gbenchmark)',
8+
yield : true,
9+
)

src/libstore-tests/package.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mkMesonExecutable (finalAttrs: {
3535
../../.version
3636
./.version
3737
./meson.build
38-
# ./meson.options
38+
./meson.options
3939
(fileset.fileFilter (file: file.hasExt "cc") ./.)
4040
(fileset.fileFilter (file: file.hasExt "hh") ./.)
4141
];

0 commit comments

Comments
 (0)