Skip to content

Add a Speed and testing section to the README - #1147

Merged
derek73 merged 6 commits into
masterfrom
docs/readme-speed-testing
Nov 8, 2026
Merged

derek73 merged 6 commits into
masterfrom
docs/readme-speed-testing

Conversation

@derek73

@derek73 derek73 commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Adds a short Speed and testing section to README.rst (the PyPI project page), right after "How well it reads names". It also carries, in a commit of its own (aa001cc, then amended by the review rounds), four doc fixes from a post-merge review of #1146: what a strict language choice keeps, stated reading by reading (docs/customize.rst, docs/AGENTS.md's Claims rule, docs/locales.rst's strict paragraph, strict_pool's docstring).

Where each figure comes from

All measured 2026-11-07 on this branch (master at 83e499d plus this PR's docs-only commits), Apple M3 Pro, Python 3.11.17.

Per-parse cost: about 90 µs a name, about 11,000 names a second. One default Parser(), parsing every non-empty name in tools/differential/snapshot.jsonl (7,106 entries, 6,897 distinct names; 209 entries repeat a name under another name_order, and the recipe ignores the order). One warm-up pass, then the median of seven timed passes. Runs: 89.4, 91.4 and 91.2 µs; reviewers measured 92.5–97.1 µs on a loaded machine. The same script on 3.14.7 gave 83.1 µs. The corpus includes CLDR's fictional renderings, so this is a timing workload and makes no accuracy claim.

import json, statistics, time
from nameparser import Parser
names = [r["name"] for r in map(json.loads, filter(None, open(
    "tools/differential/snapshot.jsonl", encoding="utf-8", newline="").read().split("\n")))
    if r.get("name")]
parser = Parser()
for n in names: parser.parse(n)          # warm-up
runs = []
for _ in range(7):
    t = time.perf_counter()
    for n in names: parser.parse(n)
    runs.append(time.perf_counter() - t)
med = statistics.median(runs)
print(len(names), f"{med/len(names)*1e6:.1f} us/parse", f"{len(names)/med:,.0f} names/s")

Parser build cost: in a fresh process, after one parse():

Build Time
First parser_for of a language choice (index load plus compile) 37–51 ms (PT or ES first)
A different choice after it 6.8–7.4 ms
The same choice again (compile_choice is cached) about 1.2 ms
A default Parser() about 11 µs

The first row is mostly the one-time index() load, which a reviewer measured at 27.5 ms.

Tests: 40,256 collected (uv run pytest --collect-only -q -n0 | grep -c ::); 39,652 under tests/v2/. A full run without the extra gave 40,162 passed, 29 skipped and 5 xfailed. Of the 29 skips, 25 need the ja extra; the other 4 are test_parser.py:2045 rows that skip everywhere.

Scaling shapes: 32 clock-timed shapes, len(_SHAPES) + len(_PREFIXED_SHAPES) + len(_POLICY_SHAPES) = 23 + 7 + 2, timed at base and base * _FACTOR (4), and failing when the ratio is not below _MAX_RATIO = 6.0.

Other claims, checked against the tree

  • No runtime dependencies: pyproject.toml has dependencies = []. Per uv.lock, the ja extra's namedivider-python brings numpy, pandas, scipy, lightgbm, regex and typer.
  • Threads: restates docs/concepts.rst "Parsers are values" and customize.rst. Pickling: round-trips of Parser(), parser_for(locales.ES) and parser_for(locales.ZH) checked. ja_segmenter() returns a nested closure, so it cannot pickle.
  • CI: the build matrix (3.11–3.15) runs the parallel pass, then the serial pass. ja-extra (3.11, 3.15) runs tests/v2/. source-tree runs from a git archive export without nameparser installed.
  • Every >>> example runs: CI runs sphinx-build -b doctest docs and python -m doctest README.rst. Every >>> under docs/ sits in a .. doctest:: block.
  • deviates:: per docs/design/rules/README.md, the runner asserts today's output. No rule carries a marker today, so the README gives no count.
  • Strict choices: verified with parser_for(Locale(code=..., lexicon=Lexicon.empty(), languages=frozenset({...})), strict=...):
    • JOHN SMITH MBA, ING and John Smith Junior (all mul) still read as suffix and report under strict nl, hi and de.
    • Strict nl reads JOHN SMITH MA as family MA and Ahmed bin Ali as middle bin; ar.toml is a non-Latin file whose Latin-letter readings strict drops.
    • Strict nl reads JAN I SMITH unreported; plain nl reports conjunction-or-initial.
    • Strict pt reads Do Smith as family Do Smith.
    • Strict en keeps the Devanagari गुरु.

Checks: python -m doctest README.rst, PyPI's readme_renderer, sphinx-build -b doctest and -b html all clean; tests/v2/test_doc_citations.py passes.

Reviews

  1. Round 1, on 3c1fe54 (README): pr-review-toolkit:code-reviewer and pr-review-toolkit:comment-analyzer.
    • The linear-growth claim was broader than the tests guard.
    • The "Measured" lead covered bullets that are not measurements.
    • The measured tree was not named.
    • The count was 6,897 distinct names, not 7,106.
    • The CI jobs do not all run every test, and "nothing installed" was wrong.
    • The ja extra pulls a numeric stack, and its segmenter does not pickle.
    • All of these were fixed in 3b1beb4.
  2. Round 1, on aa001cc (strict wording): pr-review-toolkit:comment-analyzer. Strict tests the word's script, not the file's (ar's bin); mul keeps readings, not words (i); the both-in-mul case was missing; the strict_pool docstring was ambiguous. Fixed in a151952.
  3. Round 2, on 3b1beb4 and a151952: pr-review-toolkit:comment-analyzer.
    • The parser build cost had been measured on the cached path.
    • The ja skips were mis-stated.
    • The scaling bound was vague.
    • bin was filed under name readings.
    • customize.rst's premise did not name mul.
    • locales.rst's strict paragraph described a word at a time. Fixing it was a sweep this PR had missed.
    • All fixed in be0e045, which also corrected the skip count from 29 to 25 after a full run.
  4. Round 3, on be0e045: pr-review-toolkit:comment-analyzer.
    • The first-build cost is mostly the index load, and a second choice costs a few ms.
    • "The tree that became 3.0" claimed a release that does not exist yet.
    • customize.rst's "keeping only the mul file's" read as dropping the chosen files.
    • Wording fixes.
    • All fixed in af7431c.

Follow-up, not in this PR: "native-script vocabulary", which leaves native-script name readings unsaid, also appears in Lexicon.strict's docstring (_lexicon.py), parser_for's docstring (_parser.py), root AGENTS.md, the UNCHOOSABLE comment, mechanisms.md, the vocabulary-model RFC and decisions/vocabulary-model's Standing summary. None of these is false about vocabulary.

🤖 Generated with Claude Code

Per-parse cost measured over the committed corpus snapshot, the
no-dependency and reuse-one-Parser notes, and how the test suite holds
readings in place (snapshot, doctests, rules with deviates: markers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73 derek73 added this to the 3.0 milestone Nov 8, 2026
@derek73 derek73 added the docs Documentation fixes and updates label Nov 8, 2026
@derek73 derek73 self-assigned this Nov 8, 2026
derek73 and others added 5 commits November 7, 2026 21:01
…pts included

A strict choice keeps mul.toml's readings and native-script ones whatever
file holds them (strict_kept), so JOHN SMITH MBA, John Smith Junior and
JOHN SMITH ING still read by the count and report under strict nl, hi or
de. customize.rst's two strict sentences and docs/AGENTS.md's Claims rule
said it kept only the chosen files; the rule now also says
readings_of() leaves out readings off by default (es lady given) and
adds a both-in-mul word to the layouts to test. strict_pool's docstring
said native-script vocabulary where it keeps native-script name
readings too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of aa001cc: strict_kept tests whether the WORD is native script,
so a non-Latin file's readings of a Latin word leave too (ar.toml's bin:
strict nl reads Ahmed bin Ali middle bin). The mul clause now names the
file's readings rather than whole words (i has a mul reading beside ca's
and pl's), the both-in-mul case joins customize.rst's unsettled forks,
and strict_pool's docstring states its three sources unambiguously.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of 3c1fe54: the measurement moves into the speed bullet and
names the tree; the linear claim is stated for the 32 shapes the scaling
tests repeat; the snapshot count names its 6,897 distinct names; the ja
extra brings namedivider's numeric stack, and its segmenter does not
pickle; building a locale parser costs about twenty parses (a default
Parser() costs less than one, so the reuse reason is stated for a
locale); the CI bullet says which jobs run which tests; known gaps only
carry deviates: markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: the reason to reuse a parser is the first build of a language
choice (tens of ms for ES, over a hundred parses; a repeat about 1 ms);
25 tests need the ja extra and skip in the build matrix; the scaling
bound is stated (more than six times, repeated part four times as long);
namedivider's dependencies named; the tree dated as the one that became
3.0. Strict wording: customize.rst's premise names mul and its clause
says whose readings are dropped; AGENTS.md files bin under the
vocabulary half and states the script predicate (a letter, no Latin
one); locales.rst's strict paragraph now speaks reading by reading
(Catalan's and Polish's i leave though mul holds the numeral).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: the first build of a language choice is mostly the one-time
reading-index load (tens of ms); another choice after it costs a few ms.
The tree is the 3.0 development tree again (no tag yet). customize.rst's
strict clause keeps the chosen files' readings explicitly. locales.rst
and AGENTS.md wording fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73
derek73 merged commit ccaf6ad into master Nov 8, 2026
13 checks passed
@derek73
derek73 deleted the docs/readme-speed-testing branch November 8, 2026 05:27
derek73 added a commit that referenced this pull request Nov 8, 2026
* Say what a strict choice keeps of a native-script word

A strict choice keeps a native-script word's readings, name readings
included, exactly as a plain choice of the same languages keeps them:
a reading written default = false stays off unless its file is chosen
(strict pt reads गुरु प्रसाद शर्मा title गुरु). Seven sites said
"native-script vocabulary", leaving the name readings unsaid
(Lexicon.strict, parser_for and its refusal, the UNCHOOSABLE comment,
AGENTS.md, mechanisms.md, decisions/vocabulary-model's Standing
summary, the RFC), and #1146/#1147's customize.rst and docs/AGENTS.md
said a word in another script keeps "every file's" readings.

Also from the post-merge review of af7431c: the README's first-build
cost of a choice includes the reading index only unless a parse has
already loaded it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Record the native-script clarification, and fix the review findings

A dated decisions entry (2026-11-07 #1148) states what a strict choice
keeps of a native-script word, with head notes on the 2026-10-30
file-strict and 2026-11-02 #1005 entries and the Standing lines
pointing at it, so a Standing line no longer says what no entry says.

The RFC's overview, its strict-hu and strict-ko sentences, its
no-language refusal sentence and its operations-table row now say
native-script words' readings; its File-strict paragraph's verb agrees
again. The README's first-build sentence loads, not compiles, the
reading index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Write the native-script entry as a partial supersession

decisions/README.md allows four kinds of edit to an entry, and
"clarified by" is none of them; the 2026-10-30 entry's item 1 also
says name readings leave. So the new entry SUPERSEDES both clauses in
part, and the two heads say so in the sanctioned form. The entry names
the filter as it was at 3j (strict_pool's) and since #1001
(strict_kept). The RFC's Status paragraph and phasing row get the same
parenthetical as its overview, and strict hu's readings are the
default's, a plain hu choice being refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mark the 3j head's native-script wording superseded by #1148 too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Restore the comma in the #1148 entry's SUPERSEDES list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation fixes and updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant