with one click
update-benchmarks
// Use when benchmark numbers in docs/performance.rst need refreshing, after performance changes, before releases, or when the user asks to update benchmarks.
// Use when benchmark numbers in docs/performance.rst need refreshing, after performance changes, before releases, or when the user asks to update benchmarks.
| name | update-benchmarks |
| description | Use when benchmark numbers in docs/performance.rst need refreshing, after performance changes, before releases, or when the user asks to update benchmarks. |
Regenerate all benchmark data and update docs/performance.rst.
Also update docs/index.rst, docs/faq.rst, and README.md with derived numbers.
Run these sequentially (not in parallel โ concurrent builds cause /dev/null permission errors):
# 1a. Main comparison (accuracy, speed, memory) โ chardet + charset-normalizer + cchardet
uv run python scripts/compare_detectors.py --memory --cn --cchardet --mypyc
# 1b. chardet 6.0.0 comparison (accuracy, speed only โ memory is very slow for 6.0.0)
uv run python scripts/compare_detectors.py -c 6.0.0 --mypyc
# 1c. charset-normalizer dataset subset
uv run python scripts/compare_detectors.py --cn-dataset --cn --mypyc
# 1d. Cross-version mypyc (CPython only โ PyPy can't do mypyc)
uv run python scripts/compare_detectors.py --python 3.10 --python 3.11 --python 3.12 --python 3.13 --python 3.14 --mypyc
# 1e. Cross-version pure (all interpreters)
uv run python scripts/compare_detectors.py --python 3.10 --python 3.11 --python 3.12 --python 3.13 --python 3.14 --python pypy3.10 --python pypy3.11 --pure
# 1f. Thread safety (wall-clock times โ use detection: field, not sum of per-file times)
for py in 3.13 3.14 3.13t 3.14t; do
for build in "--pure" "--mypyc"; do
for threads in 1 2 4 8; do
echo "=== $py $build threads=$threads ==="
uv run python scripts/compare_detectors.py --python "$py" $build --threads "$threads" 2>&1 | grep 'detection:'
done
done
done
Memory benchmarks are off by default (pass --memory to include them). Step 1a includes --memory for chardet, charset-normalizer, and cchardet. Step 1b runs chardet 6.0.0 without memory because its memory benchmark is extremely slow. If you need chardet 6.0.0 memory numbers, add --memory to step 1b.
From the main comparisons (1a + 1b), extract:
X/2521 = XX.X% for each detector2521 / total_secondsX/2513 = XX.X% for each detectorFrom thread safety (1f), extract wall-clock detection time (the (detection: X.XXs) field), NOT the sum-of-per-file-times in the timing distribution.
Update all tables and derived comparison text:
mime_type key if missing)uv run sphinx-build -W docs docs/_build
git add docs/performance.rst docs/index.rst docs/faq.rst README.md
git commit -m "docs: update benchmark numbers for 7.X.0"
git push
compare_detectors.py caches results in .benchmark_results/. Cache keys include the detector version, Python version, build type (pure/mypyc), thread count, and a content hash of the benchmark scripts (benchmark_time.py, benchmark_memory.py, utils.py), equivalence rules (equivalences.py), and the test-data submodule commit. Results auto-invalidate when any of these change. The chardet version includes the git commit hash (e.g., 7.2.1.dev25+g3680cc1ad), so any chardet commit invalidates the local chardet cache, and any test-data change invalidates all caches. The --cn-dataset flag doesn't need its own cache key because the benchmark subprocess always runs on all files; the subset filter is applied when aggregating results. Only use --no-cache if you need to re-benchmark an unchanged version (e.g., to reduce measurement noise).--memory to include them. Only step 1a needs memory.--mypyc (mypyc is CPython-only). Always use --pure for PyPy.--python is repeatable: --python 3.12 --python 3.13 runs both sequentially.