Skip to content

perf: reduce per-call overhead in gin_helper hot paths#51615

Merged
MarshallOfSound merged 1 commit into
43-x-yfrom
trop/43-x-y-bp-perf-reduce-per-call-overhead-in-gin_helper-hot-paths-1778708897776
May 18, 2026
Merged

perf: reduce per-call overhead in gin_helper hot paths#51615
MarshallOfSound merged 1 commit into
43-x-yfrom
trop/43-x-y-bp-perf-reduce-per-call-overhead-in-gin_helper-hot-paths-1778708897776

Conversation

@trop

@trop trop Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Backport of #51596

See that PR for details.

Notes: Improved performance of native event emission, IPC dispatch, and option-dictionary parsing.

Several gin_helper code paths run on every native event emit, every IPC
message, every option-dictionary parse, and every C++ -> JS callback.
This change removes a handful of avoidable per-call costs from those
paths.

EmitEvent / CallMethodWithArgs:
- Internalize the event name and the "emit" method name with
  gin::StringToSymbol so V8 dedupes them against its string table and
  property lookups can compare by pointer identity instead of hashing a
  fresh kNormal string on every emit.
- Reuse the emitter object as the node::CallbackScope async resource
  instead of allocating a throwaway v8::Object per emit.
- Drop the redundant inner EscapableHandleScope; EmitEvent / CustomEmit
  already open one immediately around the call.

gin_helper::Dictionary:
- Internalize property keys via gin::StringToSymbol (matching what
  gin::DataObjectBuilder and gin::ObjectTemplateBuilder already do).
- Replace the unconditional Has() + Get() pair in Get() with a single
  GetRealNamedProperty() lookup — one V8 boundary crossing per key
  instead of two, with the same missing-vs-undefined disambiguation.
  Native interceptor objects (process.env, etc.) are detected with the
  zero-cost HasNamedLookupInterceptor() hidden-class flag and routed to
  the Has()/Get() path that drives the interceptor.

gin_helper::Locker:
- Store v8::Locker inline in std::optional instead of heap-allocating it
  per construction; this is on the path of every C++ -> JS callback.

blink converters:
- char16_t::FromV8: read the UTF-16 code unit directly instead of
  round-tripping V8's internal UTF-16 through UTF-8 and back.

Microbenchmark medians (Linux x86-64 testing build):

  EmitEvent (1 listener)              2806 ns -> 1816 ns  (-35%)
  EmitEvent (no listeners)            2788 ns -> 1800 ns  (-35%)
  Dictionary::Get (per key, all hit)  82.2 ns -> 26.9 ns  (-67%)
  Dictionary::Get (per key, all miss) 55.6 ns -> 18.5 ns  (-67%)
  Dictionary::Set (per key)           121  ns -> 82.6 ns  (-32%)
  gin_helper::Locker ctor/dtor        94.3 ns -> 9.85 ns  (-90%)
  C++ -> JS callback (RepeatingCb)    463  ns -> 301  ns  (-35%)

Co-authored-by: Sam Attard <sattard@anthropic.com>
@trop trop Bot requested a review from MarshallOfSound May 13, 2026 21:48
@trop trop Bot added 43-x-y backport This is a backport PR semver/none labels May 13, 2026
@MarshallOfSound MarshallOfSound enabled auto-merge (squash) May 13, 2026 22:33
@MarshallOfSound MarshallOfSound merged commit 00db1a5 into 43-x-y May 18, 2026
132 of 138 checks passed
@MarshallOfSound MarshallOfSound deleted the trop/43-x-y-bp-perf-reduce-per-call-overhead-in-gin_helper-hot-paths-1778708897776 branch May 18, 2026 20:45
@release-clerk

release-clerk Bot commented May 18, 2026

Copy link
Copy Markdown

Release Notes Persisted

Improved performance of native event emission, IPC dispatch, and option-dictionary parsing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants