feat: add benchmark target to Makefile (#14)
## Description This PR adds benchmarking capabilities to the lambda interpreter. The benchmarks measure performance across all sample files in the samples folder. This enables consistent performance testing and helps track optimization improvements over time. Changes in this PR: - Added new `bench` target to Makefile for running Go benchmarks. - Created `benchmark_test.go` with sub-benchmarks for each sample file (Church, Fast, Saccharine, Simple, Thunk). - Used `b.Run` for organizing sample-specific sub-benchmarks and `b.Loop` for efficient iteration. - Configured benchmarks to use fixed iterations (10x) and 4 CPU cores for reproducible results. ### Decisions Used `b.Loop()` instead of traditional `for i := 0; i < b.N; i++` pattern. This is the modern Go benchmarking idiom that provides better performance measurement. Benchmarks run the full pipeline (parse, compile, execute, stringify) to measure end-to-end performance for each sample. ## Benefits Provides quantitative performance metrics for the lambda interpreter. Enables tracking performance improvements or regressions across different sample complexities. Consistent benchmark configuration (fixed iterations, CPU cores) ensures reproducible results for comparison. ## Checklist - [x] Code follows conventional commit format. - [x] Branch follows naming convention (`<type>/<description>`). Always use underscores. - [x] Tests pass (if applicable). - [x] Documentation updated (if applicable). Reviewed-on: #14 Co-authored-by: M.V. Hutz <git@maximhutz.me> Co-committed-by: M.V. Hutz <git@maximhutz.me>
This commit was merged in pull request #14.
This commit is contained in:
6
Makefile
6
Makefile
@@ -1,7 +1,7 @@
|
||||
BINARY_NAME=lambda
|
||||
TEST=simple
|
||||
|
||||
.PHONY: help build run profile explain graph docs clean
|
||||
.PHONY: help build run profile explain graph docs bench clean
|
||||
.DEFAULT_GOAL := help
|
||||
.SILENT:
|
||||
|
||||
@@ -13,6 +13,7 @@ help:
|
||||
echo " explain - Build and run with explanation mode and profiling"
|
||||
echo " graph - Generate and open CPU profile visualization"
|
||||
echo " docs - Start local godoc server on port 6060"
|
||||
echo " bench - Run benchmarks for all samples"
|
||||
echo " clean - Remove all build artifacts"
|
||||
|
||||
build:
|
||||
@@ -37,6 +38,9 @@ docs:
|
||||
echo ">>> View at 'http://localhost:6060/pkg/git.maximhutz.com/max/lambda/'"
|
||||
go run golang.org/x/tools/cmd/godoc@latest -http=:6060
|
||||
|
||||
bench:
|
||||
go test -bench=. -benchtime=10x -cpu=4 ./cmd/lambda
|
||||
|
||||
clean:
|
||||
rm -f ${BINARY_NAME}
|
||||
rm -f program.out
|
||||
|
||||
70
cmd/lambda/benchmark_test.go
Normal file
70
cmd/lambda/benchmark_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.maximhutz.com/max/lambda/internal/config"
|
||||
"git.maximhutz.com/max/lambda/internal/engine"
|
||||
"git.maximhutz.com/max/lambda/pkg/convert"
|
||||
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||
"git.maximhutz.com/max/lambda/pkg/saccharine"
|
||||
)
|
||||
|
||||
// Helper function to run a single sample through the lambda interpreter.
|
||||
func runSample(samplePath string) error {
|
||||
// Read the sample file.
|
||||
input, err := os.ReadFile(samplePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse code into syntax tree.
|
||||
ast, err := saccharine.Parse(string(input))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compile expression to lambda calculus.
|
||||
compiled := convert.SaccharineToLambda(ast)
|
||||
|
||||
// Create minimal config for benchmarking.
|
||||
cfg := &config.Config{
|
||||
Source: config.StringSource{Data: ""},
|
||||
Destination: config.StdoutDestination{},
|
||||
Profile: "",
|
||||
Explanation: false,
|
||||
Statistics: false,
|
||||
Verbose: false,
|
||||
}
|
||||
|
||||
// Create and run the engine.
|
||||
process := engine.New(cfg, &compiled)
|
||||
process.Run()
|
||||
|
||||
// Get final result (to ensure it's not optimized away).
|
||||
_ = lambda.Stringify(compiled)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Benchmark all samples using sub-benchmarks.
|
||||
func BenchmarkSamples(b *testing.B) {
|
||||
samples := map[string]string{
|
||||
"Church": "../../samples/church.txt",
|
||||
"Fast": "../../samples/fast.txt",
|
||||
"Saccharine": "../../samples/saccharine.txt",
|
||||
"Simple": "../../samples/simple.txt",
|
||||
"Thunk": "../../samples/thunk.txt",
|
||||
}
|
||||
|
||||
for name, path := range samples {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
if err := runSample(path); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user