Skip to content

perf(android): faster object lifecycle and call paths in the JNI bridge - #134

Open
ammarahm-ed wants to merge 16 commits into
perf/jni-10-arraybuffer-fromfrom
perf/jni-11-upstream-2099
Open

ammarahm-ed wants to merge 16 commits into
perf/jni-10-arraybuffer-fromfrom
perf/jni-11-upstream-2099

Conversation

@ammarahm-ed

Copy link
Copy Markdown
Collaborator

Ports the Java ↔ JS bridge and first-access work of NativeScript/android#2099 into both the Node-API and the direct runtime, adapted to the existing wrapper lifecycle, and fixes the construction-window id misroute.

Changes

  • Call arguments cross as one long[] (values, then one type byte per argument) read with a single region copy
  • One dense class index per object, so native caches the JNI class name and global ref instead of calling Class.getName() and FindClass per new wrapper; GetOrCreateJSWrapper collapses the id lookup and wrapper creation
  • ObjectTable replaces the four id maps (and the three NativeScript*Map classes), with optimistic StampedLock reads on API 24+
  • Finalized ids are queued and handed to Java in one makeInstancesWeak call per post-GC drain
  • registerConstructedInstance returns used memory in the same upcall
  • Only an instance of exactly the class JS is constructing takes the reserved object id
  • Bindings carry runtimeTag__ns and objectId__ns, used as the receiver hint; runtimes are indexed by id
  • MethodNameIds gives each callback name a process-wide id and native keeps one name value per runtime
  • Large static-field sets use dictionary mode and their final fields become read-only values on first read
  • Instance methods and accessors are non-constructors on the direct runtime (V8)
  • Base-method lookup by hash, allocation-free signature lookup, one thread-local for the JNIEnv cache, -Bsymbolic-functions

Not ported: the admission gate, instance-template wrappers, internalized names from the metadata table, the unconverted-argument cleanup (no equivalent in this tree), and non-constructor methods on Node-API (napi_create_function has no option for it).

Before / after

Release build, V8, Sony SO-01M, 2 launches after a warm-up, base is the stack top (#132), same harness on both sides. Differences under ~5% are noise. ns per call unless marked.

Node-API base Node-API this PR Direct base Direct this PR
Median of all 54 rows −19.5% −23.1%
Static final field read 387 12 340 12
First access android.R.attr (ms) 21.8 7.7 30.2 11.8
First access android.view (ms) 6.4 5.0 12.5 7.3
First access TextView (ms) 2.7 2.3 4.6 3.1
Invoke int callback 14,175 8,393 14,859 8,227
Invoke void callback 8,223 5,722 8,012 4,693
Return a new object 13,374 9,990 13,435 10,013
Call a method on a new object 20,322 14,883 20,825 14,412
Return an Int Array 27,804 18,474 20,871 14,211
Void static method 311 266 316 268
Package member read 4.2 4.0 4.1 3.0
Native heap after run (MB) 207.5 205.0 174.0 176.5

Large packages are deliberately not put in dictionary mode: it made package member reads 3x slower (4 → 12 ns) for a small first-access gain.

The harness has no wrapper-release or multithreaded rows, so the batching and ObjectTable changes are not measured here.

Tests

Jasmine suite on the device with V8: Node-API 529 specs, 0 failures; direct 457 specs, 0 failures (stack top: 522/0 and 450/0). New specs cover wrapper identity, construction windows and a callback that throws after a GC. The NativeScriptRuntimeBound interface count in the generated-class specs is updated. ObjectTableTest and MethodNameIdsTest run in the runtime unit tests.

Only V8 was built and run. static-binding-generator GeneratorTest fails on the base as well (ClassFormatError) and could not be run. The runtime generator's dex output for the new binding fields was compile-checked only.

Stacked on #132.

@coderabbitai

coderabbitai Bot commented Nov 11, 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: 8e5c248a-de49-4e04-b22a-8f5f3ce4818a

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

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