Canon includes a comprehensive performance benchmarking system to prevent regressions in XML/HTML parsing and comparison algorithms.
1. Running Benchmarks
# Run all benchmarks (10s per test, ~5 minutes total)
bundle exec rake performance:run
# Quick benchmark (2s per test, ~30 seconds)
bundle exec rake performance:quick
# Compare against main branch (for PRs, fails on regression)
bundle exec rake performance:compare
# Run specific category
bundle exec rake performance:category[xml_parsing]
# Output in different formats
bundle exec rake performance:json
bundle exec rake performance:yaml
2. Benchmark Categories
2.1. XML Parsing
-
DOM (simple): Standard Nokogiri DOM parsing
-
SAX (simple): SAX-based streaming parser
-
DOM (large): Large document DOM parsing
-
SAX (large): Large document SAX parsing
SAX parser is typically ~20-50% faster than DOM for XML parsing.
2.2. HTML Parsing
-
Simple HTML: Basic HTML document parsing
-
Complex HTML: HTML with scripts, styles, and tables
3. SAX Parser
Canon includes a SAX-based XML parser (Canon::Xml::SaxBuilder) that provides
significantly faster XML parsing by avoiding intermediate Nokogiri DOM trees.
3.1. How It Works
Traditional parsing:
XML String → Nokogiri DOM (~60ms) → Canon::Xml::Node (~1200ms) = ~1260ms
SAX parsing:
XML String → Nokogiri SAX → Canon::Xml::Node (~200ms) = ~200ms
4. CI Integration
Performance benchmarks run automatically on:
4.1. Pull Requests
When a PR is opened, the performance workflow compares benchmarks against the main branch.
If any benchmark regresses by more than 10%, the check fails with a clear error message.
# CI output shows comparison
Comparing against: Previous branch (main).
Threshold: 10% regression allowed
XML Parsing: SAX (large)
base: 1042.30 IPS
curr: 1285.20 IPS
change: +23.31%
✅ OK
5. Threshold Configuration
The default regression threshold is 10%. Configure via rake task:
# Custom threshold (e.g., 5%)
RUBYOPT="-rbenchmark-runner" bundle exec rake performance:compare
Or modify lib/tasks/performance_comparator.rb:
DEFAULT_THRESHOLD = 0.10 # 10%
6. Adding New Benchmarks
Add test methods to lib/tasks/benchmark_runner.rb:
# In BENCHMARKS hash
BENCHMARKS = {
xml_parsing: [
# ... existing tests ...
{ name: "New Test", method: :my_new_test, desc: "Description" },
],
}.freeze
# Add test method
def my_new_test
xml = DataGenerator.generate_xml(items: @items)
measure { Canon::Xml::SaxBuilder.parse(xml) }
end