A pytest fixture for benchmarking code that automatically calibrates test runs for accurate performance measurements.
—
pytest-benchmark provides extensive configuration options through pytest command-line arguments, configuration files, and test markers. You can control timing parameters, output formats, storage options, and benchmark behavior.
@pytest.mark.benchmark(**kwargs)
def test_function(benchmark):
"""
Mark a test with custom benchmark settings.
Supported kwargs:
max_time (float): Maximum time per test in seconds
min_rounds (int): Minimum number of rounds
min_time (float): Minimum time per round in seconds
timer (callable): Timer function to use
group (str): Benchmark group name
disable_gc (bool): Disable garbage collection
warmup (bool): Enable warmup rounds
warmup_iterations (int): Number of warmup iterations
calibration_precision (int): Calibration precision factor
cprofile (bool): Enable cProfile integration
"""# Timing parameters
--benchmark-min-time SECONDS # Minimum time per round (default: 0.000005)
--benchmark-max-time SECONDS # Maximum time per test (default: 1.0)
--benchmark-min-rounds NUM # Minimum rounds (default: 5)
--benchmark-timer FUNC # Timer function (default: platform default)
--benchmark-calibration-precision NUM # Calibration precision (default: 10)# Warmup configuration
--benchmark-warmup KIND # Warmup mode: auto/on/off (default: auto)
--benchmark-warmup-iterations NUM # Max warmup iterations (default: 100000)# Benchmark execution
--benchmark-disable-gc # Disable GC during benchmarks
--benchmark-skip # Skip benchmark tests
--benchmark-disable # Disable benchmarking (run once)
--benchmark-enable # Force enable benchmarks
--benchmark-only # Only run benchmark tests# Result display
--benchmark-sort COL # Sort column: min/max/mean/stddev/name/fullname
--benchmark-group-by LABEL # Grouping: group/name/fullname/func/fullfunc/param
--benchmark-columns LABELS # Comma-separated column list
--benchmark-name FORMAT # Name format: short/normal/long/trial
--benchmark-verbose # Verbose output
--benchmark-quiet # Quiet mode# Storage options
--benchmark-storage URI # Storage URI (default: file://./.benchmarks)
--benchmark-netrc FILE # Netrc file for credentials
--benchmark-save NAME # Save results with name
--benchmark-autosave # Auto-save with timestamp
--benchmark-save-data # Include timing data in saves# Export formats
--benchmark-json PATH # Export to JSON
--benchmark-csv FILENAME # Export to CSV
--benchmark-histogram FILENAME # Generate histograms# Profiling
--benchmark-cprofile COLUMN # Enable cProfile with sort column
--benchmark-cprofile-loops LOOPS # cProfile iteration count
--benchmark-cprofile-top COUNT # Number of profile rows to show
--benchmark-cprofile-dump PREFIX # Save cProfile dumps# Result comparison
--benchmark-compare NUM # Compare against run number
--benchmark-compare-fail EXPR # Fail on performance regression@pytest.mark.benchmark(group="string_ops", min_rounds=10)
def test_string_processing(benchmark):
def process_text(text):
return text.upper().replace(' ', '_')
result = benchmark(process_text, "hello world")
assert result == "HELLO_WORLD"@pytest.mark.benchmark(
max_time=2.0, # Run for up to 2 seconds
min_rounds=5, # At least 5 rounds
min_time=0.01 # Each round at least 10ms
)
def test_slow_operation(benchmark):
def slow_function():
time.sleep(0.01)
return sum(range(1000))
result = benchmark(slow_function)
assert result == 499500import time
@pytest.mark.benchmark(timer=time.process_time)
def test_cpu_intensive(benchmark):
def cpu_work():
return sum(x**2 for x in range(10000))
result = benchmark(cpu_work)
assert result == 333283335000@pytest.mark.benchmark(group="database")
def test_db_insert(benchmark):
def insert_data():
# Simulate database insert
return "inserted"
result = benchmark(insert_data)
assert result == "inserted"
@pytest.mark.benchmark(group="database")
def test_db_select(benchmark):
def select_data():
# Simulate database select
return ["row1", "row2"]
result = benchmark(select_data)
assert len(result) == 2# Run all tests with benchmarks
pytest --benchmark-only
# Skip benchmark tests
pytest --benchmark-skip
# Run with custom timing
pytest --benchmark-min-rounds=10 --benchmark-max-time=2.0# Sort by mean time, show specific columns
pytest --benchmark-sort=mean --benchmark-columns=min,max,mean,stddev
# Group by test function name
pytest --benchmark-group-by=func
# Verbose output with detailed timing
pytest --benchmark-verbose# Save baseline results
pytest --benchmark-save=baseline
# Compare against baseline
pytest --benchmark-compare=baseline
# Auto-save with timestamp
pytest --benchmark-autosave
# Fail if performance regresses by more than 5%
pytest --benchmark-compare-fail=mean:5%# Export to multiple formats
pytest --benchmark-json=results.json \
--benchmark-csv=results.csv \
--benchmark-histogram=charts
# Generate cProfile data
pytest --benchmark-cprofile=cumtime \
--benchmark-cprofile-top=10 \
--benchmark-cprofile-dump=profiles[tool:pytest]
addopts =
--benchmark-min-rounds=5
--benchmark-sort=min
--benchmark-group-by=group
--benchmark-columns=min,max,mean,stddev,median,ops,rounds
# Storage configuration
benchmark_storage = file://.benchmarks
benchmark_autosave = true[tool.pytest.ini_options]
addopts = [
"--benchmark-min-rounds=5",
"--benchmark-sort=min",
"--benchmark-group-by=group"
]
benchmark_storage = "file://.benchmarks"
benchmark_autosave = trueimport time
def custom_timer():
"""Custom high-precision timer."""
return time.perf_counter()
@pytest.mark.benchmark(timer=custom_timer)
def test_with_custom_timer(benchmark):
result = benchmark(lambda: sum(range(1000)))
assert result == 499500# File storage (default)
--benchmark-storage=file://.benchmarks
--benchmark-storage=file:///absolute/path/benchmarks
# Elasticsearch storage
--benchmark-storage=elasticsearch+http://localhost:9200/benchmarks/pytest
--benchmark-storage=elasticsearch+https://user:pass@host:9200/index/type
# With authentication
--benchmark-storage=elasticsearch+https://host:9200/index \
--benchmark-netrc=~/.netrc# Override via environment
export PYTEST_BENCHMARK_DISABLE=1
export PYTEST_BENCHMARK_STORAGE="file:///tmp/benchmarks"
pytest # Uses environment settings# conftest.py
def pytest_configure(config):
# Programmatic configuration
config.option.benchmark_min_rounds = 10
config.option.benchmark_sort = 'mean'
config.option.benchmark_group_by = 'group'# Fail if mean increases by more than 5%
--benchmark-compare-fail=mean:5%
# Fail if minimum time increases by more than 100ms
--benchmark-compare-fail=min:0.1
# Multiple thresholds
--benchmark-compare-fail=mean:10% --benchmark-compare-fail=max:20%# Supported comparison expressions:
"mean:5%" # Percentage increase in mean
"min:0.001" # Absolute increase in seconds
"max:10%" # Percentage increase in max
"stddev:50%" # Percentage increase in stddev- name: Run benchmarks
run: |
pytest --benchmark-only \
--benchmark-json=benchmark.json \
--benchmark-compare-fail=mean:10%
- name: Upload benchmark results
uses: actions/upload-artifact@v2
with:
name: benchmark-results
path: benchmark.json# Save baseline in master branch
pytest --benchmark-save=master --benchmark-json=master.json
# Compare feature branch
pytest --benchmark-compare=master \
--benchmark-compare-fail=mean:15% \
--benchmark-json=feature.jsonInstall with Tessl CLI
npx tessl i tessl/pypi-pytest-benchmark