Skip to content

Render inline tables in O(n) instead of O(n²)#525

Open
tfoutrein wants to merge 1 commit into
python-poetry:masterfrom
AstekGroup:perf/inline-table-render
Open

Render inline tables in O(n) instead of O(n²)#525
tfoutrein wants to merge 1 commit into
python-poetry:masterfrom
AstekGroup:perf/inline-table-render

Conversation

@tfoutrein

Copy link
Copy Markdown
Contributor

What

InlineTable.as_string() recomputed, on every separator comma, two any(self._value.body[i + 1:]) scans of the remaining body — "is there a following Null?" and "is there a following key?" — to decide whether a dangling separator comma should be dropped. On an inline table with many keys that is O(n²) in the number of elements.

The two predicates only depend on the position of the last real key and the last Null (deleted) element, both of which can be found once. Precompute them in a single pass — together with has_explicit_commas, which already needed a full pass — and turn the two per-comma scans into O(1) index comparisons:

has_following_null = last_null_idx > i
has_following_key = last_item_idx is not None and last_item_idx > i

The render is now O(n).

Benchmark

as_string() of an inline table with n keys (median, interleaved A/B vs master):

n keys speedup
50 ~3.9×
200 ~11×
800 ~41×

master is quadratic (~0.23 → 2.7 → 41 ms/render); the patched render is linear. No effect on small inline tables or other rendering.

Tests

No behaviour change: the precomputed predicates are exactly equivalent to the tail scans. Full suite incl. the toml-test conformance submodule passes. The delicate part is the deferred-separator / removed-key comma logic, so round-trip output was verified byte-identical — including edited inline tables (keys added/removed, trailing/leading commas, comments, dotted/nested) — by a differential over ~7k generated inline tables with del/__setitem__ mutations. Added a focused regression test pinning the rendered output after key deletions.

@tfoutrein tfoutrein force-pushed the perf/inline-table-render branch from e8f3f3b to 33b0903 Compare June 18, 2026 06:06
InlineTable.as_string() recomputed, on every separator comma, two
any(body[i + 1:]) tail scans (is there a following Null / a following key?)
to decide whether to drop a dangling separator. On an inline table with many
keys that is O(n^2).

Precompute the needed facts in a single pass instead -- whether any
explicit-comma whitespace is present, the index of the last real key, and the
index of the last Null (deleted) element -- and turn the two per-comma scans
into O(1) index comparisons. The render is now O(n).

No behaviour change: the precomputed predicates are exactly equivalent to the
tail scans. Full suite incl. toml-test conformance passes; round-trip output
is byte-identical -- including edited inline tables (keys added/removed,
trailing/leading commas, comments, dotted/nested) -- verified by a
differential over ~7k generated inline tables with del/setitem mutations.
~4x faster rendering a 50-key inline table, ~40x at 800 keys.
@tfoutrein tfoutrein force-pushed the perf/inline-table-render branch from 33b0903 to 414199e Compare June 18, 2026 06:15
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.

1 participant