Skip to content

perf(android): create ArrayBuffer.from without calling ByteBuffer.isDirect - #132

Open
ammarahm-ed wants to merge 2 commits into
fix/android-exception-handlesfrom
perf/jni-10-arraybuffer-from
Open

ammarahm-ed wants to merge 2 commits into
fix/android-exception-handlesfrom
perf/jni-10-arraybuffer-from

Conversation

@ammarahm-ed

Copy link
Copy Markdown
Collaborator

Port of NativeScript/android#2076 to both the Node-API and the direct runtime.

ArrayBuffer.from(byteBuffer) called ByteBuffer.isDirect() through JNI to choose between the zero-copy path and the copying path. GetDirectBufferAddress already returns null for a heap buffer, so the null check now picks the path and the Java call, its cached method id and the m_isDirectMethodID field are gone. A direct buffer whose address is unavailable falls back to the copying path, which works for both kinds.

In the Node-API tree the nativeObject key is now set with napi_set_named_property instead of creating a string value for it on every call. The upstream change also caches that key as a v8::Eternal, which has no counterpart here, and the direct runtime already sets the property by name.

The two new benchmark cases are in the first commit.

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 Δ
ArrayBuffer.from direct ByteBuffer 3,953 2,851 -28% 3,921 3,714 -5%
ArrayBuffer.from heap ByteBuffer 69,733 71,165 +2% 5,487 5,354 -2%

Only the direct-buffer rows go through the code this removes a JNI call from. The heap-buffer rows are within noise. The Node-API heap-buffer case is about 70 µs per call in both builds because its copying path makes several JNI calls; that is not changed here.

All cases
Case Node-API before after Δ Direct before after Δ
Void Method on instance 644 656 +2% 638 636 -0%
Field on instance 823 874 +6% 962 895 -7%
Int Field on instance 387 382 -1% 508 414 -19%
Get Static Field 853 817 -4% 863 802 -7%
Void Static Method 313 300 -4% 324 326 +1%
multiply(a, b) 457 482 +6% 556 514 -8%
Pass and return string 1,157 1,157 -0% 1,184 1,199 +1%
Return a String 800 799 -0% 777 792 +2%
Return an Int 314 312 -1% 329 332 +1%
Pass a String 651 625 -4% 706 712 +1%
Return a Boolean 307 305 -0% 322 333 +3%
Return a Double 392 378 -4% 392 435 +11%
Pass an Int 381 371 -3% 455 429 -6%
Pass a Double 387 369 -4% 466 425 -9%
Pass a Boolean 456 453 -1% 457 468 +2%
Return an Int Array 29,129 28,003 -4% 20,304 20,314 +0%
Pass an Int Array 2,879 2,726 -5% 3,614 3,597 -0%
Return a String Array 30,559 29,472 -4% 21,434 21,560 +1%
Pass a String Array 5,640 5,543 -2% 6,787 6,863 +1%
Return a Double Array 28,541 27,698 -3% 21,019 20,927 -0%
Pass a Double Array 2,932 2,813 -4% 3,678 3,681 +0%
Return a Boolean Array 27,663 26,760 -3% 20,211 20,113 -0%
Pass a Boolean Array 3,259 2,968 -9% 3,875 3,847 -1%
Int Array[0] read 362 348 -4% 240 243 +2%
String Array[0] read 802 801 -0% 708 680 -4%
Double Array[0] read 436 458 +5% 340 344 +1%
Boolean Array[0] read 337 337 -0% 236 237 +1%
Int Array[0] write 456 461 +1% 391 383 -2%
String Array[0] write 1,149 1,134 -1% 1,066 1,110 +4%
Double Array[0] write 483 490 +1% 393 382 -3%
Boolean Array[0] write 470 488 +4% 392 391 -0%
Return a Date Object 15,497 14,592 -6% 14,307 14,388 +1%
Pass a Date Object 3,961 3,728 -6% 3,896 3,628 -7%
Invoke void callback 7,646 7,587 -1% 6,950 6,905 -1%
Instance add(a, b) 795 819 +3% 845 812 -4%
Pass and return non-ASCII string 1,221 1,174 -4% 1,213 1,196 -1%
Pass and return 1KB string 4,943 4,985 +1% 5,167 5,097 -1%
Return a Char 360 359 -0% 370 361 -2%
Return this 1,278 1,316 +3% 1,335 1,344 +1%
Return a new object 13,985 13,609 -3% 12,916 12,638 -2%
Call a method on a new object 20,271 20,226 -0% 19,995 19,736 -1%
Pass a 16 Int Array 2,983 2,765 -7% 3,754 3,587 -4%
Pass a 16 Double Array 3,458 3,334 -4% 4,278 4,339 +1%
Invoke int callback 14,405 13,503 -6% 12,539 11,824 -6%
Invoke string callback 8,806 9,178 +4% 8,563 9,897 +16%
ArrayBuffer.from direct ByteBuffer 3,953 2,851 -28% 3,921 3,714 -5%
ArrayBuffer.from heap ByteBuffer 69,733 71,165 +2% 5,487 5,354 -2%
Invoke callback that throws 164,400 164,714 +0% 245,558 244,388 -0%
Native heap after run (MB) 200 MB 203 MB +2% 181 MB 181 MB +0%

Only V8 was built and run. These numbers come from builds of this change on top of an earlier revision of the PR below it, which does not touch this code path.

The Jasmine suite on a debug build of this branch, same device: Node-API 522 specs, 0 failures; direct 450 specs, 0 failures (main: 523 and 0; 451 and 1 failure in a timing spec).

@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: e4dd1ee4-3d6c-4ba9-86d6-ea9db7055c0c

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 08:37

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