This directory contains benchmarking and profiling tools for the Liquid Go template engine. The suite simulates real-world Shopify template rendering to provide realistic performance measurements.
The performance suite includes:
- Theme-based benchmarks: Renders actual Shopify theme templates with realistic data
- Unit benchmarks: Tests individual components (lexer, expression parser)
- CPU profiling: Identifies performance bottlenecks
- Memory profiling: Tracks memory allocations and usage
performance/
├── theme_runner.go # Core infrastructure for loading and rendering templates
├── benchmark_test.go # Main benchmark suite (tokenize, parse, render)
├── unit_test.go # Unit benchmarks (expression, lexer)
├── profile.go # CPU/memory profiling tool
├── memory_profile.go # Detailed memory profiling tool
├── README.md # This file
└── shopify/ # Shopify-specific implementations
├── liquid.go # Tag and filter registration
├── database.go # Test data loader
├── comment_form.go # CommentForm tag
├── paginate.go # Paginate tag
├── json_filter.go # JSON filter
├── money_filter.go # Money formatting filters
├── shop_filter.go # Shop-related filters
├── tag_filter.go # Tag management filters
└── weight_filter.go # Weight conversion filters
The easiest way to run benchmarks and save results for comparison:
cd performance
./run_benchmark.shThis automatically saves results with timestamp and commit hash to benchmark_results/.
Run all benchmarks with default settings:
go test -bench=.Use the PHASE environment variable to run specific phases:
# Tokenization only
PHASE=tokenize go test -bench=.
# Parsing only
PHASE=parse go test -bench=.
# Rendering only (using pre-compiled templates)
PHASE=render go test -bench=.
# Parse and render together
PHASE=run go test -bench=.Control benchmark behavior with standard Go testing flags:
# Run for longer duration (more accurate results)
go test -bench=. -benchtime=10s
# Run with memory allocation stats
go test -bench=. -benchmem
# Run specific benchmarks
go test -bench=BenchmarkRender
go test -bench=BenchmarkExpressionGenerate and analyze CPU profiles:
# Generate CPU profile
go run profile.go -cpuprofile=cpu.prof -iterations=200
# Analyze with pprof (interactive)
go tool pprof cpu.prof
# Generate visual graph (requires graphviz)
go tool pprof -http=:8080 cpu.profCommon pprof commands:
top- Show top CPU consumerslist <function>- Show annotated source for a functionweb- Generate visual call graph (requires graphviz)
Generate and analyze memory profiles:
# Generate memory profile
go run profile.go -memprofile=mem.prof -iterations=200
# Analyze with pprof
go tool pprof mem.prof
# Quick memory stats
go run memory_profile.goProfile both CPU and memory in one run:
go run profile.go -cpuprofile=cpu.prof -memprofile=mem.prof -iterations=500The suite includes focused benchmarks for individual components:
Test the expression parser with different input types:
# All expression benchmarks
go test -bench=BenchmarkExpression
# Specific types
go test -bench=BenchmarkExpressionParseString
go test -bench=BenchmarkExpressionParseVariable
go test -bench=BenchmarkExpressionParseNumber
go test -bench=BenchmarkExpressionParseRangeTest the lexer/tokenizer:
go test -bench=BenchmarkLexerThe benchmarks use realistic e-commerce data from reference-liquid/performance/:
-
Templates: Real Shopify theme templates from
tests/directory- dropify/ - Minimalist theme
- ripen/ - Standard theme
- tribble/ - Complex theme with search
- vogue/ - Fashion-focused theme
-
Database:
shopify/vision.database.ymlcontains:- Products with variants, pricing, inventory
- Collections and categories
- Blog posts and articles
- Navigation links and menus
- Shopping cart data
The suite includes Shopify-specific implementations:
paginate- Pagination for collectionsform(comment_form) - Comment form generation
json- JSON encodingmoney,money_with_currency- Price formattingweight,weight_with_unit- Weight conversionasset_url,global_asset_url,shopify_asset_url- Asset URL generationproduct_img_url- Product image URL with sizinglink_to,link_to_vendor,link_to_type- Link generationlink_to_tag,link_to_add_tag,link_to_remove_tag- Tag linksdefault_pagination- Pagination HTMLpluralize- Singular/plural word selection
Go benchmark results show:
- ns/op: Nanoseconds per operation (lower is better)
- B/op: Bytes allocated per operation (lower is better)
- allocs/op: Number of allocations per operation (lower is better)
Example:
BenchmarkRender-8 100 12345678 ns/op 1234567 B/op 12345 allocs/op
The Ruby implementation uses benchmark/ips (iterations per second), while Go uses nanoseconds per operation:
Ruby: 1000 i/s = 1ms per iteration
Go: 1000000 ns/op = 1ms per iteration
To convert:
ns/optoi/s:1000000000 / ns_per_opi/stons/op:1000000000 / iterations_per_sec
Install benchstat for statistical comparison:
go install golang.org/x/perf/cmd/benchstat@latestCompare two benchmark runs:
./compare_benchmarks.sh baseline_results.txt benchmark_results/bench_LATEST.txtExample output:
name old time/op new time/op delta
Tokenize-10 541µs ± 0% 530µs ± 0% -2.03%
Parse-10 6.58ms ± 0% 6.45ms ± 0% -1.97%
Render-10 10.5ms ± 0% 10.2ms ± 0% -2.86%
- Baseline:
baseline_results.txtcontains initial results - History: See
BENCHMARK_HISTORY.mdfor notable performance changes - Scripts:
run_benchmark.sh- Run and save benchmarkscompare_benchmarks.sh- Compare two result files
# Before making changes
./run_benchmark.sh
# Note the filename: benchmark_results/bench_20251118_143022_abc123.txt
# Make your code changes
# ...
# After changes
./run_benchmark.sh
# Compare
./compare_benchmarks.sh \
benchmark_results/bench_20251118_143022_abc123.txt \
benchmark_results/bench_20251118_150433_def456.txtSee BENCHMARKING.md for detailed guide on tracking and analyzing performance.
If you get errors about missing templates, ensure you have the reference repository:
# From liquidgo root
git clone https://github.com/Shopify/liquid reference-liquidThe benchmarks require the liquid package to be importable. Run from the performance directory:
cd performance
go mod init github.com/pierre/liquidgo/performance # if needed
go test -bench=.For large profiling runs, you may need to increase available memory:
GOGC=50 go run profile.go -iterations=1000When adding new benchmarks:
- Mirror Ruby tests: Check
reference-liquid/performance/for equivalent Ruby benchmarks - Use realistic data: Leverage the existing test templates and database
- Document clearly: Add comments explaining what's being measured
- Verify results: Compare with Ruby implementation where possible
Target performance characteristics (compared to Ruby implementation):
- Parse: Should be 2-5x faster than Ruby
- Render: Should be 3-10x faster than Ruby
- Memory: Should use 50-70% less memory than Ruby
Run comparative benchmarks regularly to track progress toward these goals.