Skip to content

perf(runtime): reuse wrappers before resolving runtime classes - #40

Merged
triniwiz merged 1 commit into
mainfrom
perf/interface-return-cache
Nov 6, 2026
Merged

triniwiz merged 1 commit into
mainfrom
perf/interface-return-cache

Conversation

@triniwiz

@triniwiz triniwiz commented Nov 6, 2026

Copy link
Copy Markdown
Member

Interface-typed and Object returns looked up the object's runtime class (GetRuntimeClassName plus a metadata lookup) before checking the wrapper cache. The lookup came in with #37, so since then, returning an object that already had a JS wrapper paid for it on every call, and interface-typed returns paid twice. In a JsonArray.GetAt loop the lookup was about 25% of the time, and about half of that was HSTRING::to_string, which goes through core::fmt one character at a time.

Changes (classic + napi)

  • Cache first. Classic return paths (return_value_from_kind and the ns_proxy builders) check wrapper_cache before resolving the class. New helpers: wrapper_cache::get_for takes an IUnknown, and take_cached takes an owned return pointer and releases it on a hit.
  • napi. Cache entries record whether the wrapper was built for the object's class (is_class), so an interface-typed return reuses it by COM identity without asking for the runtime class.
  • Name decoding. Runtime class names use HSTRING::to_string_lossy (a direct UTF-16 decode).
  • Removed a GetRuntimeClassName call in method_call.rs whose result was discarded.

Benchmarks

Classic V8 playground, medians of alternating A/B runs against main:

op main this PR
JsonArray.GetAt (cached element) 1.98M/s 4.77M/s (2.4x)
JsonObject.GetNamedValue 2.49M/s 2.88M/s
static call, set_get_named, construct unchanged (within noise)

Testing

  • cargo test -p runtime --lib -- projection_test instance_cache wrapper: 11/11.
  • Both engines type-check (classic, napi_engine).

Interface-typed and Object returns looked up the object's runtime class
(GetRuntimeClassName, a metadata lookup) before checking the wrapper
cache, so returning an already-wrapped object paid for both on every
call, twice for interface-typed returns. The cache is now checked first
on both engines; napi records which cached wrappers were built for the
object's class so an interface-typed return can reuse them.

Runtime class names convert from UTF-16 directly instead of formatting
one character at a time, which was about half of the lookup's cost.

JsonArray.GetAt on cached elements: 2.0M -> 4.8M calls/s (classic V8).
@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 30cba31a-5c4c-483f-8133-77f5fcc4d83d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@triniwiz
triniwiz merged commit 6dbfb90 into main Nov 6, 2026
3 checks passed
@triniwiz
triniwiz deleted the perf/interface-return-cache branch November 6, 2026 07:19
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