Skip to content

perf(android): resolve a method's declared return class once per call site - #125

Open
ammarahm-ed wants to merge 1 commit into
perf/jni-05-direct-signaturefrom
perf/jni-06-return-class
Open

ammarahm-ed wants to merge 1 commit into
perf/jni-05-direct-signaturefrom
perf/jni-06-return-class

Conversation

@ammarahm-ed

Copy link
Copy Markdown
Collaborator

Wrapping an object a Java method returned looked up its class by name on every call: GetObjectClass, Class.getName() through a Java up-call, a string replace, and a metadata lookup keyed by that string.

When a method's declared return type is the object's exact runtime class, which is the common case, none of that is needed. The declared class and its MetadataNode are now resolved once per call site and stored on MetadataEntry (returnClass, returnNode, returnClassResolved). Each call compares the returned object's class to the cached one with a single IsSameObject and builds the wrapper from the cached node. A polymorphic return, where the runtime class is a subclass of the declared one, falls back to the name lookup as before. Array returns, whose declared type is not an L descriptor, never use the cache.

ObjectManager::CreateJSWrapper gets an overload that takes the node and class directly, in both the Node-API and the direct runtime.

Results

V8 release build on a Sony SO-01M (Android 11, arm64), AOT-compiled with cmd package compile -m speed. One warm-up launch is discarded, then 5 launches; values are the median ns per call.

Case Node-API before after Δ Direct before after Δ
Return a new object 31,356 28,552 -9% 23,765 22,441 -6%
Call a method on a new object 39,072 36,425 -7% 33,952 31,500 -7%
Return a Date Object 30,484 28,744 -6% 24,658 23,343 -5%
Return this 1,248 1,320 +6% 1,279 1,327 +4%
Return a String 812 808 -0% 794 781 -2%

Object returns that create a new wrapper are 5–9% faster, which is at the edge of this device's noise for allocation-heavy rows (up to ±15%). Return this hands back an existing wrapper and does not reach this code. The cost that dominates a new-object return is the wrapper itself, which the later PRs in this stack address.

All cases
Case Node-API before after Δ Direct before after Δ
Void Method on instance 671 664 -1% 575 606 +5%
Field on instance 824 810 -2% 860 845 -2%
Int Field on instance 380 376 -1% 368 372 +1%
Get Static Field 834 826 -1% 879 846 -4%
Void Static Method 346 317 -8% 303 304 +0%
multiply(a, b) 510 528 +4% 537 511 -5%
Pass and return string 1,211 1,208 -0% 1,144 1,184 +4%
Return a String 812 808 -0% 794 781 -2%
Return an Int 339 335 -1% 312 304 -3%
Pass a String 703 677 -4% 665 657 -1%
Return a Boolean 334 326 -2% 307 300 -2%
Return a Double 409 400 -2% 386 364 -6%
Pass an Int 436 422 -3% 414 418 +1%
Pass a Double 428 402 -6% 434 409 -6%
Pass a Boolean 500 502 +1% 446 429 -4%
Return an Int Array 28,417 27,806 -2% 18,199 18,681 +3%
Pass an Int Array 3,356 3,266 -3% 4,116 4,138 +1%
Return a String Array 29,365 28,082 -4% 19,965 20,078 +1%
Pass a String Array 5,624 5,468 -3% 6,732 6,578 -2%
Return a Double Array 28,102 26,814 -5% 19,561 19,978 +2%
Pass a Double Array 3,249 3,214 -1% 3,941 3,952 +0%
Return a Boolean Array 27,043 25,587 -5% 18,685 19,182 +3%
Pass a Boolean Array 3,162 3,102 -2% 3,793 3,760 -1%
Int Array[0] read 366 343 -6% 259 250 -4%
String Array[0] read 771 787 +2% 684 666 -3%
Double Array[0] read 444 435 -2% 362 361 -0%
Boolean Array[0] read 349 352 +1% 253 234 -7%
Int Array[0] write 452 459 +1% 377 364 -4%
String Array[0] write 1,155 1,138 -2% 1,077 1,058 -2%
Double Array[0] write 476 476 +0% 386 372 -3%
Boolean Array[0] write 478 466 -2% 390 372 -5%
Return a Date Object 30,484 28,744 -6% 24,658 23,343 -5%
Pass a Date Object 3,931 3,865 -2% 3,597 3,657 +2%
Invoke void callback 7,254 7,288 +0% 6,884 6,887 +0%
Instance add(a, b) 855 854 -0% 799 816 +2%
Pass and return non-ASCII string 1,228 1,189 -3% 1,141 1,177 +3%
Pass and return 1KB string 5,065 5,073 +0% 4,997 5,133 +3%
Return a Char 373 361 -3% 341 355 +4%
Return this 1,248 1,320 +6% 1,279 1,327 +4%
Return a new object 31,356 28,552 -9% 23,765 22,441 -6%
Call a method on a new object 39,072 36,425 -7% 33,952 31,500 -7%
Pass a 16 Int Array 4,558 4,543 -0% 5,152 5,141 -0%
Pass a 16 Double Array 5,130 5,304 +3% 5,796 5,780 -0%
Invoke int callback 10,264 10,346 +1% 10,772 11,232 +4%
Invoke string callback 8,610 8,676 +1% 8,336 8,262 -1%
Native heap after run (MB) 257 MB 257 MB +0% 183 MB 208 MB +14%

Only V8 was built and run.

@coderabbitai

coderabbitai Bot commented Nov 9, 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: 3eb0ec50-2d35-443f-b243-45855fc7f457

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

@ammarahm-ed
ammarahm-ed added this pull request to stack #121 November 9, 2026 06:38

This branch has not been deployed

No deployments
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