Skip to content

Precompute per-parameter interop binding flags#2719

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:perf/interop-parameter-flags
Jul 21, 2026
Merged

Precompute per-parameter interop binding flags#2719
lahma merged 1 commit into
sebastienros:mainfrom
lahma:perf/interop-parameter-flags

Conversation

@lahma

@lahma lahma commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Part of the V8-gap performance campaign (#1775 follow-up). Profiling interop-method-calls showed MethodInfoFunction.TryCall at 15.9% self — and most of it is constant-per-method work re-derived on every call.

Change

Interop argument binding re-derived three per-parameter-type facts per argument per call: typeof(JsValue).IsAssignableFrom(parameterType), the generic-shape test hidden inside IsGenericParameter (which forced a boxing argument.ToObject() detour for every argument, including plain int parameters), and the JsValue[] params-signature check.

MethodDescriptor now classifies each parameter once into InteropParameterFlags; MethodInfoFunction.TryCall and MethodDescriptor.Call branch on the cached flags. Resolved generic methods re-classify their own parameters (their ParameterInfos differ from the descriptor's). The general conversion branch computes the boxed argument lazily now that the generic probe no longer produces it as a side effect. Conversion semantics are unchanged — the same conversions run in the same order; only constant-per-method reflection checks and the needless pre-boxing are gone.

Numbers (default BDN job, same-base A/B)

Row base change delta
SingleOverload_NoArg 2.183 us 1.960 us −10.2%
SingleOverload_OneIntArg 2.393 us / 2.89 KB 2.346 us / 1.95 KB −2.0% / −33% alloc
SingleOverload_OneDoubleArg 2.593 us 2.529 us −2.5%
SingleOverload_OneStringArg 2.617 us 2.564 us −2.0%

Three rows (TwoArgs, Overloaded, Params) initially read +3–4% on single launches; a launchCount-5 verification on both sides shows them statistically tied (+0.2%, +0.3%, −2.1%) — layout bimodality, not regressions.

Gates

Rebased on current main (includes #2716#2718): Jint.Tests 3658/3595 (net10/net472), PublicInterface 114/114 both TFMs, Test262 99,431 passed / 0 failed.

🤖 Generated with Claude Code

Interop argument binding re-derived three per-parameter-type facts on
every call: typeof(JsValue).IsAssignableFrom(parameterType), the
generic-shape test hidden inside IsGenericParameter (which forced a
boxing argument.ToObject() detour for every argument, including plain
int parameters), and the JsValue[] params-signature check.

MethodDescriptor now classifies each parameter once into
InteropParameterFlags; MethodInfoFunction.TryCall and
MethodDescriptor.Call branch on the cached flags. Resolved generic
methods re-classify their own parameters (their ParameterInfos differ
from the descriptor's). The general conversion branch computes the
boxed argument lazily now that the generic probe no longer produces it
as a side effect. Conversion semantics are unchanged - the same
conversions run, only constant-per-method reflection checks and the
needless pre-boxing are gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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