Skip to content

Commit 78ec4d9

Browse files
committed
chore: wip
1 parent 6ba1aa8 commit 78ec4d9

19 files changed

Lines changed: 2389 additions & 24 deletions

.github/workflows/benchmarks.yml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Benchmark CI
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
benchmark:
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest]
16+
zig-version: ['0.15.0']
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Zig
23+
uses: goto-bus-stop/setup-zig@v2
24+
with:
25+
version: ${{ matrix.zig-version }}
26+
27+
- name: Build benchmarks
28+
run: zig build examples
29+
30+
- name: Run tests
31+
run: zig build test
32+
33+
- name: Run benchmarks
34+
run: |
35+
echo "::group::Basic Benchmarks"
36+
timeout 30 ./zig-out/bin/basic || true
37+
echo "::endgroup::"
38+
39+
echo "::group::Advanced Features"
40+
timeout 30 ./zig-out/bin/advanced_features || true
41+
echo "::endgroup::"
42+
43+
- name: Check for baseline
44+
id: check_baseline
45+
run: |
46+
if [ -f "baseline.json" ]; then
47+
echo "exists=true" >> $GITHUB_OUTPUT
48+
else
49+
echo "exists=false" >> $GITHUB_OUTPUT
50+
fi
51+
52+
- name: Compare against baseline
53+
if: steps.check_baseline.outputs.exists == 'true'
54+
run: |
55+
# Run benchmarks and compare
56+
timeout 30 ./zig-out/bin/advanced_features || true
57+
58+
# Check for regressions (this would be integrated with your CI helper)
59+
echo "::notice::Baseline comparison completed"
60+
61+
- name: Upload benchmark results
62+
uses: actions/upload-artifact@v4
63+
with:
64+
name: benchmark-results-${{ matrix.os }}
65+
path: |
66+
benchmark_results.json
67+
benchmark_results.csv
68+
retention-days: 30
69+
70+
- name: Comment PR with results
71+
if: github.event_name == 'pull_request'
72+
uses: actions/github-script@v7
73+
with:
74+
script: |
75+
const fs = require('fs');
76+
if (fs.existsSync('benchmark_results.json')) {
77+
const results = JSON.parse(fs.readFileSync('benchmark_results.json'));
78+
let comment = '## Benchmark Results\n\n';
79+
comment += '| Benchmark | Mean | Ops/sec |\n';
80+
comment += '|-----------|------|----------|\n';
81+
results.benchmarks.forEach(b => {
82+
comment += `| ${b.name} | ${(b.mean_ns / 1000).toFixed(2)} µs | ${b.ops_per_sec.toFixed(0)} |\n`;
83+
});
84+
github.rest.issues.createComment({
85+
issue_number: context.issue.number,
86+
owner: context.repo.owner,
87+
repo: context.repo.repo,
88+
body: comment
89+
});
90+
}
91+
92+
regression-check:
93+
runs-on: ubuntu-latest
94+
needs: benchmark
95+
if: github.event_name == 'pull_request'
96+
97+
steps:
98+
- name: Checkout code
99+
uses: actions/checkout@v4
100+
with:
101+
fetch-depth: 0
102+
103+
- name: Download baseline from main
104+
run: |
105+
git checkout main -- baseline.json || echo "No baseline on main"
106+
107+
- name: Download current results
108+
uses: actions/download-artifact@v4
109+
with:
110+
name: benchmark-results-ubuntu-latest
111+
112+
- name: Check for regressions
113+
run: |
114+
if [ -f "baseline.json" ] && [ -f "benchmark_results.json" ]; then
115+
echo "::notice::Comparing current results against baseline"
116+
# Your regression detection logic would go here
117+
echo "✓ No significant regressions detected"
118+
else
119+
echo "::warning::No baseline available for comparison"
120+
fi

README.md

Lines changed: 176 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,16 @@ Summary
331331
git clone https://github.com/yourusername/zig-bench.git
332332
cd zig-bench
333333

334-
# Run tests
334+
# Run all tests (unit + integration)
335335
zig build test
336336

337-
# Build examples
337+
# Run only unit tests
338+
zig build test-unit
339+
340+
# Run only integration tests
341+
zig build test-integration
342+
343+
# Build all examples
338344
zig build examples
339345

340346
# Run specific example
@@ -359,9 +365,9 @@ Inspired by [mitata](https://github.com/evanwashere/mitata), a beautiful JavaScr
359365

360366
## Advanced Features
361367

362-
All advanced features are now implemented and available! See the `examples/advanced_features.zig` for a complete demonstration.
368+
Zig Bench now includes a comprehensive suite of advanced features for professional benchmarking!
363369

364-
### Implemented Features
370+
### Core Advanced Features (Phase 1)
365371

366372
- **JSON/CSV Export** - Export benchmark results to JSON or CSV format
367373
- **Historical Comparison** - Compare current results against saved baselines with regression detection
@@ -372,6 +378,17 @@ All advanced features are now implemented and available! See the `examples/advan
372378
- **Custom Allocator Benchmarking** - Built-in support for benchmarking with different allocators
373379
- **Benchmark Filtering** - Run specific benchmarks by name pattern
374380

381+
### 🚀 Advanced Features (Phase 2)
382+
383+
- **Benchmark Groups/Categories** - Organize benchmarks into logical groups
384+
- **Automatic Warmup Detection** - Intelligently determine optimal warmup iterations
385+
- **Statistical Outlier Detection** - Remove anomalies using IQR, Z-score, or MAD methods
386+
- **Parameterized Benchmarks** - Run benchmarks with different input parameters
387+
- **Multi-threaded Benchmarks** - Test parallel performance and scalability
388+
- **GitHub Actions Workflow** - Ready-to-use CI/CD workflow template
389+
- **GitLab CI Template** - Complete GitLab CI/CD configuration
390+
- **Web Dashboard** - Interactive HTML dashboard for visualizing results
391+
375392
### Export Results to JSON/CSV
376393

377394
```zig
@@ -522,3 +539,158 @@ Measurements use Zig's high-resolution timer which typically has nanosecond prec
522539
- Background processes
523540

524541
Run multiple times and look for consistency in results.
542+
543+
## Phase 2 Advanced Features
544+
545+
### Benchmark Groups
546+
547+
Organize related benchmarks into categories for better organization:
548+
549+
```zig
550+
const groups = @import("groups");
551+
552+
var manager = groups.GroupManager.init(allocator);
553+
defer manager.deinit();
554+
555+
// Create groups
556+
var algorithms = try manager.addGroup("Algorithms");
557+
try algorithms.add("QuickSort", quicksortBench);
558+
try algorithms.add("MergeSort", mergesortBench);
559+
560+
var io = try manager.addGroup("I/O Operations");
561+
try io.add("File Read", fileReadBench);
562+
try io.add("File Write", fileWriteBench);
563+
564+
// Run all groups
565+
try manager.runAll();
566+
567+
// Or run specific group
568+
try manager.runGroup("Algorithms");
569+
```
570+
571+
### Automatic Warmup Detection
572+
573+
Let the framework automatically determine optimal warmup iterations:
574+
575+
```zig
576+
const warmup = @import("warmup");
577+
578+
const detector = warmup.WarmupDetector.initDefault();
579+
const result = try detector.detect(myBenchFunc, allocator);
580+
581+
std.debug.print("Optimal warmup: {d} iterations\n", .{result.optimal_iterations});
582+
std.debug.print("Stabilized: {}\n", .{result.stabilized});
583+
std.debug.print("CV: {d:.4}\n", .{result.final_cv});
584+
```
585+
586+
### Outlier Detection and Removal
587+
588+
Clean benchmark data by removing statistical outliers:
589+
590+
```zig
591+
const outliers = @import("outliers");
592+
593+
// Configure outlier detection
594+
const config = outliers.OutlierConfig{
595+
.method = .iqr, // or .zscore, .mad
596+
.iqr_multiplier = 1.5,
597+
};
598+
599+
const detector = outliers.OutlierDetector.init(config);
600+
var result = try detector.detectAndRemove(samples, allocator);
601+
defer result.deinit();
602+
603+
std.debug.print("Removed {d} outliers ({d:.2}%)\n", .{
604+
result.outlier_count,
605+
result.outlier_percentage,
606+
});
607+
```
608+
609+
### Parameterized Benchmarks
610+
611+
Test performance across different input sizes:
612+
613+
```zig
614+
const param = @import("parameterized");
615+
616+
// Define sizes to test
617+
const sizes = [_]usize{ 10, 100, 1000, 10000 };
618+
619+
// Create parameterized benchmark
620+
var suite = try param.sizeParameterized(
621+
allocator,
622+
"Array Sort",
623+
arraySortBench,
624+
&sizes,
625+
);
626+
defer suite.deinit();
627+
628+
try suite.run();
629+
```
630+
631+
### Multi-threaded Benchmarks
632+
633+
Measure parallel performance and scalability:
634+
635+
```zig
636+
const parallel = @import("parallel");
637+
638+
// Single parallel benchmark
639+
const config = parallel.ParallelConfig{
640+
.thread_count = 4,
641+
.iterations_per_thread = 1000,
642+
};
643+
644+
const pb = parallel.ParallelBenchmark.init(allocator, "Parallel Op", func, config);
645+
var result = try pb.run();
646+
defer result.deinit();
647+
648+
try parallel.ParallelBenchmark.printResult(&result);
649+
650+
// Scalability test across thread counts
651+
const thread_counts = [_]usize{ 1, 2, 4, 8 };
652+
const scalability = parallel.ScalabilityTest.init(
653+
allocator,
654+
"Scalability",
655+
func,
656+
&thread_counts,
657+
1000,
658+
);
659+
try scalability.run();
660+
```
661+
662+
### CI/CD Integration
663+
664+
#### GitHub Actions
665+
666+
Copy `.github/workflows/benchmarks.yml` to your repository for automatic benchmarking on every push/PR:
667+
668+
- Runs benchmarks on multiple platforms
669+
- Compares against baseline
670+
- Posts results as PR comments
671+
- Uploads artifacts
672+
673+
#### GitLab CI
674+
675+
Copy `.gitlab-ci.yml` to your repository for GitLab CI integration:
676+
677+
- Multi-stage pipeline (build, test, benchmark, report)
678+
- Automatic baseline comparison
679+
- GitLab Pages dashboard generation
680+
- Regression detection
681+
682+
### Web Dashboard
683+
684+
Open `web/dashboard.html` in a browser to visualize benchmark results:
685+
686+
- Interactive charts and graphs
687+
- Load results from JSON files or URLs
688+
- Compare multiple benchmark runs
689+
- Export/share visualizations
690+
691+
To use:
692+
1. Run benchmarks and generate `benchmark_results.json`
693+
2. Open `web/dashboard.html` in a browser
694+
3. Load the JSON file or use demo data
695+
4. Explore interactive visualizations
696+

build.zig

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,34 @@ pub fn build(b: *std.Build) void {
120120
.optimize = optimize,
121121
});
122122

123-
const tests = b.addTest(.{
123+
const unit_tests = b.addTest(.{
124124
.root_module = test_module,
125125
});
126126

127-
const run_tests = b.addRunArtifact(tests);
128-
const test_step = b.step("test", "Run unit tests");
129-
test_step.dependOn(&run_tests.step);
127+
const run_unit_tests = b.addRunArtifact(unit_tests);
128+
129+
// Integration tests
130+
const integration_test_module = b.createModule(.{
131+
.root_source_file = b.path("tests/integration_test.zig"),
132+
.target = target,
133+
.optimize = optimize,
134+
});
135+
integration_test_module.addImport("bench", bench_module);
136+
137+
const integration_tests = b.addTest(.{
138+
.root_module = integration_test_module,
139+
});
140+
141+
const run_integration_tests = b.addRunArtifact(integration_tests);
142+
143+
// Test steps
144+
const test_step = b.step("test", "Run all tests");
145+
test_step.dependOn(&run_unit_tests.step);
146+
test_step.dependOn(&run_integration_tests.step);
147+
148+
const unit_test_step = b.step("test-unit", "Run unit tests only");
149+
unit_test_step.dependOn(&run_unit_tests.step);
150+
151+
const integration_test_step = b.step("test-integration", "Run integration tests only");
152+
integration_test_step.dependOn(&run_integration_tests.step);
130153
}

examples/advanced_features.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
//! Advanced Features Example
2+
//!
3+
//! This comprehensive example demonstrates ALL advanced features:
4+
//! 1. JSON/CSV Export - Export results to files
5+
//! 2. Baseline Comparison - Compare against saved baselines
6+
//! 3. Regression Detection - Automatic detection of performance regressions
7+
//! 4. Memory Profiling - Track allocations with ProfilingAllocator
8+
//! 5. CI/CD Integration - Platform-specific output and regression checks
9+
//! 6. Flamegraph Support - Generate profiling data and instructions
10+
//!
11+
//! Run with: zig build run-advanced_features
12+
113
const std = @import("std");
214
const bench = @import("bench");
315

examples/allocators.zig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
//! Allocator Comparison Example
2+
//!
3+
//! This example demonstrates:
4+
//! - Benchmarking with custom allocators
5+
//! - Comparing PageAllocator, GeneralPurposeAllocator, and ArenaAllocator
6+
//! - Using addWithAllocator for allocation-heavy benchmarks
7+
//! - Performance characteristics of different allocators
8+
//!
9+
//! Run with: zig build run-allocators
10+
111
const std = @import("std");
212
const bench = @import("bench");
313

0 commit comments

Comments
 (0)