Repository navigation
perf: build object rows from a cached v8::DictionaryTemplate - #54
Merged
Merged
Conversation
arv
force-pushed
the
arv/cache-column-keys
branch
from
October 19, 2026 12:15
61c1840 to
3fa671b
Compare
arv
force-pushed
the
arv/dictionary-template-rows
branch
from
October 19, 2026 12:15
cb404a9 to
7e97ff5
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Template creation must avoid the 128–252 column range, which can trigger V8 assertion failures or unsupported behavior.
1 open issue
What changed in this PR
This PR improves object-row creation by lazily caching V8 dictionary templates while preserving fallbacks for unsupported column names.
Changes:
- Adds template creation and invalidation.
- Builds rows from cached templates.
- Expands schema-change and unusual-name tests.
| File | Summary |
|---|---|
test/23.statement.iterate.js |
Exercises template-backed iteration. |
test/22.statement.all.js |
Tests template-backed schema changes. |
test/21.statement.get.js |
Tests template reuse and unusual names. |
src/util/row-builder.cpp |
Builds rows from cached templates. |
src/util/column-keys.cpp |
Caches templates and handles fallback eligibility. |
src/objects/statement.hpp |
Updates the row-builder interface. |
src/objects/statement.cpp |
Passes context into row builders. |
Review effort: Lite
arv
force-pushed
the
arv/dictionary-template-rows
branch
2 times, most recently
from
October 19, 2026 12:28
04457d6 to
57de0a8
Compare
Contributor
Author
|
Verify Signed Commit Authors fails on Generated by Claude Code |
v8::Object::New() with a list of names makes dictionary-mode objects, which are slower to create and to read. A statement now caches a v8::DictionaryTemplate for its column names, whose instances have fast properties and share a hidden class. The template is created once the statement has built 16 object rows, since creating it costs about as much as 10-20 rows; statements that return only a few rows keep the old cost. Names the template can't take fall back to v8::Object::New(): non-ASCII names (it reads names as Latin-1), duplicate names and array indices (it crashes on both).
Instances of a v8::DictionaryTemplate with 128 to 252 names start from the dictionary map V8 uses for large object literals and then go through Map::TransitionToDataProperty, which DCHECKs that the map is not a dictionary map. Release builds produce correct rows, but they are dictionary-mode objects anyway, so the template gains nothing there. Build such rows with v8::Object::New() as before.
arv
force-pushed
the
arv/dictionary-template-rows
branch
from
October 19, 2026 12:30
57de0a8 to
115244b
Compare
This was referenced Oct 19, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Stacked on #53.
v8::Object::New()with a list of names makes dictionary-mode objects, which are slow to create and slow to read. Each statement now caches av8::DictionaryTemplatefor its column names. Instances of the template have fast properties and share one hidden class.Lazy creation. The template is built once the statement has produced 16 object rows. Building it costs about as much as 10–20 rows, and building it eagerly made one-shot statements 2–4µs slower.
Fallback. These rows keep using
v8::Object::New():Map::TransitionToDataPropertyDCHECKs against, and they would be dictionary-mode objects anyway.For every edge case tested, rows have the same own properties, order and values as before.
Invalidation. The template is dropped whenever the cached column names are refreshed: on re-prepare, or when the column count changes.
Benchmark
Same setup as #53: Node 22.22.0, 2000-row tables. Runs alternate between builds; each figure is the median of 25 rounds, in µs.
all()iterate()get()×2000all()iterate()get()×2000prepare().get()In this run, 4-column
get()came out slightly slower than #53. The gain from fast properties mostly shows when rows are read, and that benchmark reads only one property per row.Tests
"0"/"1","4294967295","-1","01","1.5";__proto__,constructor,length;Bun
Bun can't take this path either way. Bun 1.3.5 and 1.4.2 refuse to load the
better_sqlite3addon. Thetest-bunCI job runs mocha through its Node shebang, so it never actually tests Bun.🤖 Generated with Claude Code
https://claude.ai/code/session_01WCP5vbpvciRmgWBx2b1zKb