Skip to content

Add benchmark CI for release performance comparisons - #58

Closed
wbarnha wants to merge 16 commits into
masterfrom
benchmark-ci
Closed

wbarnha wants to merge 16 commits into
masterfrom
benchmark-ci

Conversation

@wbarnha

@wbarnha wbarnha commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Adds a GitHub Actions benchmark workflow for cChardet that:

  • benchmarks the current branch against the latest two non-draft, non-prerelease GitHub releases
  • uses the corpus from issue Performance regression with 3.x #57 with a pinned SHA256 check
  • fails CI when current throughput drops below 80% of the fastest compared release
  • uploads raw benchmark JSON artifacts for inspection
  • adds benchmark-action/github-action-benchmark as a complementary continuous benchmark history check

Notes

The github-action-benchmark integration records current-branch throughput in MB/s and restores/saves benchmark history through GitHub Actions cache. It writes results into the Actions job summary without requiring a gh-pages branch or extra repository write permissions on PRs.

Validation

  • Existing test suite passed locally: 125 passed, 1 skipped
  • Workflow YAML and embedded shell syntax were validated
  • Comparator fixtures passed locally
  • Small synthetic benchmark run passed locally

Full issue-corpus benchmarking was left to CI because the local Python 3.12 environment exposed an existing native allocator crash; the workflow runs on Python 3.11.

@wbarnha
wbarnha marked this pull request as ready for review July 22, 2026 12:57
wbarnha and others added 2 commits July 22, 2026 08:58
Without uchardet's generic language pass, two ranking safeguards were
lost, both now restored inside the overlay:

- nsUTF8Prober never rejects invalid byte sequences and its confidence
  floor (0.5) always clears CANDIDATE_THRESHOLD, so non-UTF-8 multibyte
  input (GBK, EUC-KR, EUC-TW, ...) was frequently mislabeled as UTF-8.
  The overlay now runs a strict incremental UTF-8 validity DFA over the
  raw stream and demotes an invalid-UTF-8 candidate to a fixed 0.25
  last-resort confidence: above nsUniversalDetector's MINIMUM_THRESHOLD
  (0.20) so near-ASCII data no other prober claims still gets the same
  weak UTF-8 guess upstream produces, below CANDIDATE_THRESHOLD (0.30)
  so it can never outrank a real candidate. The DFA state lives in the
  otherwise-unused langDetectors[0][0] slot because the upstream header
  is not overlaid and the class layout must stay unchanged.

- Upstream multiplies multibyte prober confidences by language-model
  confidence before candidate selection, which suppressed weak false
  positives such as Big5 claiming a near-ASCII Windows-1252 file at
  conf ~0.4 (issue #33). Non-UTF-8 multibyte probers now need to clear
  a 0.5 threshold on their own: genuine detections measure >= ~0.7
  while such false positives stay around 0.4.

Verified: full pytest suite passes (127 passed, 1 skipped, including
the previously failing zh/euc-tw sample and the issue #33 guard);
synthetic multi-encoding benchmark shows GBK/EUC-KR/EUC-TW mislabels
eliminated with unchanged throughput; ASan/UBSan run over the overlay
is clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xkaxun6yxtcCTsdH1GEDCM
claude and others added 3 commits July 22, 2026 19:50
The issue #57 CC-News corpus is almost entirely valid UTF-8, so cChardet's
UTF-8 fast path answers before the detection engine ever runs. That left the
exact code the encoding-only overlay changes -- and the freedesktop uchardet
engine that regressed in work item #38 -- unexercised by CI, which is why the
earlier overlay's UTF-8 mislabeling of GBK/EUC-KR text was not caught.

Add a deterministic, network-free non-UTF-8 corpus and wire it into both the
test suite and the benchmark job:

- benchmarks/make_nonutf8_corpus.py generates labeled single- and multi-byte
  non-UTF-8 documents (ISO-8859-15, Windows-1251, Shift_JIS, EUC-JP, GBK,
  Big5, EUC-KR) from short committed multilingual snippets.
- benchmark.py scores detection by decode-equivalence when the corpus is
  labeled, and counts UTF-8 mislabels (non-UTF-8 bytes reported as UTF-8 --
  the dangerous case that makes a downstream open(encoding=...) mojibake).
- compare.py renders the accuracy column and gains --max-utf8-mislabel-rate;
  the plain-list CC-News path is unchanged.
- benchmark.yml runs the three builds over the non-UTF-8 corpus and gates on
  a UTF-8 mislabel rate of 0.02 plus a lenient 0.50 throughput floor. The
  freedesktop engine is inherently slower than the old PyYoshi engine on
  non-UTF-8, so the mislabel rate -- not raw throughput -- is the signal that
  uniquely catches the overlay regression, while the throughput floor still
  trips if the language-model slowdown is reintroduced.
- src/tests/test_nonutf8_detection.py asserts, on every PR, that no multibyte
  document is mislabeled as UTF-8, that multibyte accuracy stays >= 99%, and
  that the issue #33 Big5 false positive stays suppressed.

Verified against the pre-fix overlay: the new test and the benchmark gate
both fail on it (multibyte UTF-8 mislabels, 18.7% overall mislabel rate) and
both pass on the current overlay (0 mislabels). Full suite: 130 passed,
1 skipped.
The first CI run measured the current build at 0.51x of v2.2.1 on the
non-UTF-8 corpus, right at the 0.50 floor -- flaky. That corpus of many
small documents does not reproduce uchardet #38's large-stream
language-model blow-up (the per-call overhead dominates, so the language
pass costs only ~1.6x here, not orders of magnitude), so the throughput
ratio cannot robustly separate a good build from a regressed one. Drop the
floor to 0.35 as a loose backstop for a catastrophic slowdown; the UTF-8
mislabel rate remains the primary, crisp regression signal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants