feat(bf-3gmb7): create baselines directory and JSON schema for grep-corpus benchmark

- Add schema.json with complete baseline metrics definition
  - Required fields: commit_sha, timestamp, pdftract_geomean, grep_1000_mean_ms
  - Optional fields: throughput_mb_per_sec, files_per_sec, total_runtime_sec
  - All fields include types, units, descriptions, and examples
  - Follows JSON Schema draft-07 specification

- Add README.md with comprehensive documentation
  - Purpose and use cases for baseline metrics
  - Complete field reference with types and units
  - Schema validation instructions
  - CI/CD integration guidelines
  - Performance targets from project plan
  - Update procedures and regression detection

- Validate existing main.json against schema structure
  - All required fields present and correctly formatted

Acceptance criteria:
✓ benches/baselines/ directory exists
✓ JSON schema documented with all required fields
✓ README explains baseline format and purpose
✓ Schema includes types and units for each metric
This commit is contained in:
jedarden 2026-07-06 11:13:12 -04:00
parent 5d1a2b8881
commit a749591aef
2 changed files with 241 additions and 0 deletions

145
benches/baselines/README.md Normal file
View file

@ -0,0 +1,145 @@
# Baseline Metrics
This directory stores baseline benchmark metrics for pdftract performance validation and regression tracking.
## Purpose
Baseline metrics serve as the reference point for:
- **Performance regression detection** - Compare current benchmark results against historical baselines
- **Competitive analysis** - Track pdftract performance relative to pdfminer.six, pypdf, and pdfplumber
- **CI/CD gates** - Block releases that introduce performance regressions beyond acceptable thresholds
- **Trend analysis** - Monitor performance improvements over time
## File Format
Each baseline file is a JSON document conforming to `schema.json`. The schema defines the following structure:
### Required Fields
| Field | Type | Unit | Description |
|-------|------|------|-------------|
| `commit_sha` | string | - | Git commit SHA (or "main" for tracking branch) |
| `timestamp` | string | ISO 8601 | When the baseline was recorded |
| `pdftract_geomean` | number | seconds | Geometric mean extraction time across all fixtures (pdftract) |
| `grep_1000_mean_ms` | number | milliseconds | Mean time for 1000-PDF corpus search |
### Optional Fields
| Field | Type | Unit | Description |
|-------|------|------|-------------|
| `pdfminer_geomean` | number | seconds | Geometric mean extraction time (pdfminer.six) |
| `pypdf_geomean` | number | seconds | Geometric mean extraction time (pypdf) |
| `pdfplumber_geomean` | number | seconds | Geometric mean extraction time (pdfplumber) |
| `throughput_mb_per_sec` | number | MB/s | Aggregate throughput for grep-corpus benchmark |
| `files_per_sec` | number | files/second | Processing rate for grep-corpus benchmark |
| `total_runtime_sec` | number | seconds | Wall-clock time for complete benchmark suite |
| `corpus_size` | integer | count | Number of PDF files in test corpus |
| `notes` | string | - | Free-form contextual information |
## Naming Convention
Baseline files are named by their Git branch or tag:
- `main.json` - Baseline for the main development branch
- `v0.1.0.json` - Baseline for release tag v0.1.0
- `v0.2.0.json` - Baseline for release tag v0.2.0
## Schema Validation
All baseline files should validate against `schema.json`:
```bash
# Validate a baseline file (requires ajv-cli or similar)
npx ajv validate --strict=false -s schema.json -d main.json
# Or use Python jsonschema
python - <<'PYTHON'
import jsonschema, json
with open('schema.json') as s, with open('main.json') as d:
jsonschema.validate(json.load(d), json.load(s))
PYTHON
```
## Usage in CI
The baseline metrics are used in CI to detect performance regressions:
1. **Baseline comparison**: Each benchmark run compares results against the appropriate baseline file
2. **Threshold checks**: Regressions exceeding 10% for primary metrics block the PR
3. **Competitive ratios**: pdftract must maintain ≥ 10× speedup vs pdfminer.six and ≥ 5× vs pypdf
## Performance Targets
Based on the Primary Objectives in the project plan:
| Metric | Target | Measurement |
|--------|--------|-------------|
| 100-page vector PDF, 4-core | < 3 seconds | `cargo bench`, `tests/fixtures/perf/` |
| 10-page scanned PDF (OCR) | < 30 seconds | includes Tesseract |
| Single-page extraction latency | < 150 ms p99 | wrk benchmark |
| Throughput vs pdfminer.six | ≥ 10× faster | Identical hardware |
| Throughput vs pypdf | ≥ 5× faster | Same benchmark suite |
| `pdftract grep` throughput | ≥ 50 MB/s | 1000-PDF corpus, 4-core |
## Updating Baselines
When to update a baseline:
1. **After a major release** - Create a new baseline file tagged with the release version
2. **After accepted performance improvements** - Update `main.json` when improvements merge
3. **Never for regressions** - Regressions should block release, not update baselines
Update process:
```bash
# Run benchmarks to generate new baseline
cargo bench --bench grep_corpus | tee /tmp/bench-results.txt
# Extract metrics and create/update baseline file
# (This step requires a helper script to parse benchmark output)
# Validate against schema
npx ajv validate --strict=false -s schema.json -d main.json
# Commit the updated baseline
git add benches/baselines/main.json
git commit -m "bench(bf-XXX): update main baseline after performance improvements"
```
## Example Baseline
```json
{
"commit_sha": "abc1234",
"timestamp": "2024-07-06T10:30:45Z",
"pdftract_geomean": 2.5,
"pdfminer_geomean": 28.0,
"pypdf_geomean": 15.0,
"pdfplumber_geomean": 32.0,
"grep_1000_mean_ms": 18.5,
"throughput_mb_per_sec": 87.3,
"files_per_sec": 920.0,
"total_runtime_sec": 1.09,
"corpus_size": 1000,
"notes": "Baseline for v0.2.0 release - OCR improvements and grep optimization"
}
```
## Regression Detection
The benchmark harness compares current results against baselines and flags regressions:
- **PASS**: All metrics within ±5% of baseline (acceptable variance)
- **WARN**: Metrics degraded 510% (logged, non-blocking)
- **FAIL**: Metrics degraded >10% (blocks PR merge)
- **IMPROVEMENT**: Metrics improved >5% (logged, consider baseline update)
## Historical Context
Baseline files form a historical record of pdftract's performance evolution. The `main.json` baseline represents the current state of the main branch, while tagged baselines (e.g., `v0.1.0.json`) capture performance at specific release points.
This history enables:
- Long-term performance trend analysis
- Release-to-release comparison
- Identification of performance bottlenecks
- Validation of optimization efforts

View file

@ -0,0 +1,96 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PDFtract Baseline Metrics Schema",
"description": "Schema for baseline benchmark metrics stored in benches/baselines/",
"type": "object",
"required": ["commit_sha", "timestamp", "pdftract_geomean", "grep_1000_mean_ms"],
"properties": {
"commit_sha": {
"type": "string",
"description": "Git commit SHA (short or full) for the pdftract version used",
"pattern": "^[a-fA-F0-9]{7,40}$|main$",
"examples": ["abc1234", "deadbeef1234567890", "main"]
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp when the baseline was recorded",
"examples": ["2024-01-01T00:00:00Z", "2024-07-06T10:30:45+00:00"]
},
"pdftract_geomean": {
"type": "number",
"minimum": 0,
"description": "Geometric mean extraction time in seconds across all fixtures for pdftract",
"unit": "seconds"
},
"pdfminer_geomean": {
"type": "number",
"minimum": 0,
"description": "Geometric mean extraction time in seconds across all fixtures for pdfminer.six",
"unit": "seconds"
},
"pypdf_geomean": {
"type": "number",
"minimum": 0,
"description": "Geometric mean extraction time in seconds across all fixtures for pypdf",
"unit": "seconds"
},
"pdfplumber_geomean": {
"type": "number",
"minimum": 0,
"description": "Geometric mean extraction time in seconds across all fixtures for pdfplumber",
"unit": "seconds"
},
"grep_1000_mean_ms": {
"type": "number",
"minimum": 0,
"description": "Mean time in milliseconds for searching across 1000-PDF corpus",
"unit": "milliseconds"
},
"throughput_mb_per_sec": {
"type": "number",
"minimum": 0,
"description": "Aggregate throughput in megabytes per second for grep-corpus benchmark",
"unit": "MB/s"
},
"files_per_sec": {
"type": "number",
"minimum": 0,
"description": "Files processed per second in grep-corpus benchmark",
"unit": "files/second"
},
"total_runtime_sec": {
"type": "number",
"minimum": 0,
"description": "Total wall-clock runtime in seconds for the complete benchmark suite",
"unit": "seconds"
},
"corpus_size": {
"type": "integer",
"minimum": 0,
"description": "Number of PDF files in the test corpus",
"unit": "count"
},
"notes": {
"type": "string",
"description": "Free-form notes about this baseline (e.g., placeholder, experimental, environmental factors)"
}
},
"additionalProperties": false,
"examples": [
{
"commit_sha": "main",
"timestamp": "2024-01-01T00:00:00Z",
"pdftract_geomean": 10.0,
"pdfminer_geomean": 100.0,
"pypdf_geomean": 120.0,
"pdfplumber_geomean": 150.0,
"grep_1000_mean_ms": 50.0,
"throughput_mb_per_sec": 78.5,
"files_per_sec": 850.0,
"total_runtime_sec": 1.18,
"corpus_size": 1000,
"notes": "Baseline for v0.1.0 release"
}
]
}