Skip to content

Speed up hash_no_attrs by interning element names directly - #474

Merged
ohler55 merged 2 commits into
ohler55:developfrom
Watson1978:codex/hash-no-attrs-intern
Sep 6, 2026
Merged

ohler55 merged 2 commits into
ohler55:developfrom
Watson1978:codex/hash-no-attrs-intern

Conversation

@Watson1978

@Watson1978 Watson1978 commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Ox.load(..., mode: :hash_no_attrs) creates a temporary Ruby String for every element name, associates its encoding, and immediately interns it. Repeated tags therefore allocate a new String even when the name is already interned.

Use rb_intern3() directly on the name bytes, preserving the parser's encoding and the ASCII-8BIT fallback of rb_str_new2(). This removes one temporary String per element. The change is limited to hash_no_attrs.

On Ruby 4.0.6 / arm64, parsing 40,000 records with five text fields per record:

Before After
Parse time 34.65 ms 29.78 ms 14% up
Allocated objects 480,006 240,005 1/2 down

This is a 14% reduction in parse time. Each process warmed up twice, then measured seven parses with GC.start before each measurement. Results above are the medians of four process-level medians, alternating the before/after execution order. All result digests matched.

The input was generated with:

xml = '<rows>' + '<row id="1"><name>Alice</name><city>Tokyo</city><age>30</age><status>active</status><note>hello</note></row>' * 40_000 + '</rows>'
Ox.load(xml, mode: :hash_no_attrs)

Validation:

  • Added coverage for UTF-8, Shift_JIS and binary names with both Symbol and String keys, repeated elements, XML-declared encoding, and encoded names passed to element_key_mod. These tests also pass against the original implementation.
  • Added Ox.load_file coverage for the ASCII-8BIT fallback with Symbol and String keys. The test fails when that fallback is deliberately changed to UTF-8 in an isolated build.
  • Encoding tests use the input String or XML declaration and reset default options; they also pass when run after setting the default encoding to ASCII-8BIT.
  • test/hash_load_test.rb: 15 tests, 72 assertions passed.
  • The additional *_test.rb suite (using the Rake test task's exclusions): 340 tests, 7,007 assertions passed.
  • test/tests.rb passed.
  • git diff --check passed.

@Watson1978
Watson1978 marked this pull request as ready for review September 5, 2026 12:42
@ohler55
ohler55 merged commit ae0c024 into ohler55:develop Sep 6, 2026
23 checks passed
@Watson1978
Watson1978 deleted the codex/hash-no-attrs-intern branch September 6, 2026 05:59
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