Skip to content

perf: optimize generated native dispatch#33

Open
DjDeveloperr wants to merge 22 commits into
mainfrom
perf/hermes-direct-layer-optimizations
Open

perf: optimize generated native dispatch#33
DjDeveloperr wants to merge 22 commits into
mainfrom
perf/hermes-direct-layer-optimizations

Conversation

@DjDeveloperr
Copy link
Copy Markdown
Collaborator

@DjDeveloperr DjDeveloperr commented May 22, 2026

Summary

  • Adds engine-direct native dispatch paths so V8, Hermes, QuickJS, and JSC can bypass the generic Node-API call path for generated Objective-C/C function dispatch while keeping the Node-API backend available for non-direct builds.
  • Extends generated signature dispatch with engine-specific backends, direct return handling, Hermes frame-argument dispatch, and fast block invoke support.
  • Optimizes bridge hot paths: leaner primitive/object marshalling, cached receiver/object/selector lookups, direct function/block invocation, and Hermes NSArray/NSMutableArray indexed access without the Proxy path for normal arrays.
  • Adds/updates the Objective-C dispatch benchmark harness for same-harness comparisons across NAPI engines and legacy iOS V8 AOT on/off.

Benchmark Results

Command family: node benchmarks/objc-dispatch/run.js --iterations 250000 using the same simulator/harness for all rows.

runtime total ms vs best vs legacy AOT on
v8 gsd-on 392.30 0.00 -52.13%
hermes gsd-on 437.71 +45.40 -46.59%
quickjs gsd-on 484.62 +92.32 -40.87%
v8 gsd-off 491.81 +99.51 -39.99%
hermes gsd-off 524.35 +132.04 -36.02%
quickjs gsd-off 535.76 +143.45 -34.63%
jsc gsd-on 547.74 +155.43 -33.17%
jsc gsd-off 594.84 +202.54 -27.42%
legacy aot-on 819.54 +427.24 baseline
legacy aot-off 942.25 +549.95 +14.97%

GSD deltas:

target on total ms off total ms win ms win %
v8 GSD 392.30 491.81 99.51 25.37%
hermes GSD 437.71 524.35 86.64 19.79%
quickjs GSD 484.62 535.76 51.13 10.55%
jsc GSD 547.74 594.84 47.10 8.60%
legacy AOT 819.54 942.25 122.71 14.97%

Result files:

  • build/benchmarks/objc-dispatch/results-2026-05-22T19-44-10-380Z.json
  • build/benchmarks/objc-dispatch/results-2026-05-22T19-46-55-756Z.json
  • build/benchmarks/objc-dispatch/results-2026-05-22T19-48-29-266Z.json
  • build/benchmarks/objc-dispatch/results-2026-05-22T19-49-26-650Z.json
  • build/benchmarks/objc-dispatch/results-2026-05-22T19-50-11-344Z.json

Validation

  • git diff --check origin/main..HEAD
  • IOS_TEST_ENGINE=hermes IOS_TEST_TIMEOUT_MS=900000 IOS_BUILD_TIMEOUT_MS=900000 npm run test:ios
    • SUCCESS: 705 specs, 0 failures, 14 skipped, 0 disabled
  • Built and benchmarked NAPI iOS runtime for V8, Hermes, QuickJS, and JSC simulator engines.
  • Built and benchmarked legacy iOS V8 runtime with AOT on/off.

Review Notes

  • HostObject method dispatch was investigated and rejected because method property callbacks were dramatically slower than the optimized wrapper/prototype path.
  • Hermes array optimization keeps placeholder arrays on the Proxy path and uses prototype numeric accessors only for initialized arrays.
  • Direct paths are compile-gated per engine/backend; the generic Node-API path remains for non-direct/non-engine builds.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added React Native support with TurboModule integration for iOS
    • Introduced JSI-based native API for Hermes engine
    • Added optimized fast-path invocation for Objective-C and C functions
    • Added support for engine-specific native property definitions (V8, JSC, QuickJS, Hermes)
  • Improvements

    • Enhanced callback threading and runtime unlocking mechanisms
    • Improved object reference caching and generation tracking
    • Extended dispatch signature support with engine-direct invocation
  • Documentation

    • Added React Native integration guide
    • Added benchmarking documentation and examples

@DjDeveloperr DjDeveloperr marked this pull request as ready for review May 23, 2026 07:19
@DjDeveloperr DjDeveloperr force-pushed the perf/hermes-direct-layer-optimizations branch from 55e9189 to de6be51 Compare May 23, 2026 23:59
@DjDeveloperr
Copy link
Copy Markdown
Collaborator Author

Generated by Codex. Current head: be1abb2d (fix(hermes): stabilize native callback dispatch).

Suggested human review path:

  1. Start with generated dispatch safety: metadata-generator/src/SignatureDispatchEmitter.cpp, then NativeScript/ffi/HermesFastNativeApi.mm, NativeScript/ffi/Cif.mm, NativeScript/ffi/ClassMember.mm, and NativeScript/ffi/CFunction.mm. The important bit is that Hermes frame/direct-return wrappers are only used for signatures whose args/returns can be handled without hidden cleanup or unsupported frame conversions.
  2. Review callback/threading semantics next: NativeScript/ffi/CallbackThreading.h and NativeScript/runtime/modules/timers/Timers.mm. The intended rule is: synchronous native callbacks can run JS on the native caller thread only while a native call deliberately unlocked the Hermes runtime; plain async callbacks such as dispatch_async go back through the JS run loop. Timer main-runloop work avoids dispatch_sync while the Hermes lock is already held to prevent main/background lock-order deadlocks.
  3. Review the JSI/RN interop surface: NativeScript/ffi/jsi/NativeApiJsi.mm, packages/react-native/src/index.ts, and packages/react-native/ios/NativeScriptUIView.mm. Key points are not clobbering an existing global.interop, preserving Pointer/Reference instanceof behavior through the TS wrapper, and stricter native handle parsing.
  4. Review the React Native packaging/demo/test scripts: scripts/react_native_app_utils.sh, scripts/test_react_native_ffi_compat.sh, scripts/test_react_native_turbomodule.sh, scripts/create_react_native_demo.sh, and the trusted-publishing workflow changes in .github/workflows/npm_trusted_release.yml from the prior commit. The helper script is just dedupe for app creation, pod install, simulator build, launch, and marker polling.
  5. Review RN-facing behavior through test/react-native/ffi-compat/App.tsx, the demo apps, and the @nativescript/react-native package contents/types. The FFI compat app is the most useful quick read for expected JS API shape.

Local verification run on this head:

  • git diff --check
  • bash -n scripts/react_native_app_utils.sh scripts/test_react_native_ffi_compat.sh scripts/test_react_native_turbomodule.sh scripts/create_react_native_demo.sh scripts/build_react_native_turbomodule.sh scripts/build_all_react_native.sh scripts/build_nativescript.sh && node --check scripts/run-tests-macos.js
  • MACOS_TEST_ENGINE=hermes npm run test:macos: tests=710, failures=0, errors=0, skipped=10
  • MACOS_TEST_ENGINE=v8 npm run test:macos: tests=710, failures=0, errors=0, skipped=15
  • MACOS_TEST_ENGINE=jsc npm run test:macos: tests=710, failures=0, errors=0, skipped=15
  • MACOS_TEST_ENGINE=quickjs npm run test:macos: tests=710, failures=0, errors=0, skipped=15
  • npm run build-rn-turbomodule: produced @nativescript/react-native@0.0.1 tarball.
  • npm run test-rn-turbomodule: NATIVESCRIPT_RN_TURBO_SMOKE_PASS.
  • npm run test-rn-ffi: NATIVESCRIPT_RN_FFI_COMPAT, passed=12, total=12, backend=hermes-jsi.
  • npm run benchmark:objc-dispatch: wrote build/benchmarks/objc-dispatch/results-2026-05-24T02-18-16-050Z.json; totals were napi-node (gsd-on)=2065.23ms, napi-ios (gsd-on)=418.23ms, legacy-ios (aot-on)=770.75ms.

CI for be1abb2d was still running when I posted this comment: https://github.com/NativeScript/napi-ios/actions/runs/26349525233

@DjDeveloperr
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

1 similar comment
@DjDeveloperr
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2275070d-371f-4b9f-b586-4b63e0f8b47a

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac13af and be1abb2.

📒 Files selected for processing (102)
  • .github/workflows/npm_trusted_release.yml
  • .gitignore
  • NativeScript/CMakeLists.txt
  • NativeScript/NativeScript-Prefix.pch
  • NativeScript/ffi/Block.h
  • NativeScript/ffi/Block.mm
  • NativeScript/ffi/CFunction.h
  • NativeScript/ffi/CFunction.mm
  • NativeScript/ffi/CallbackThreading.h
  • NativeScript/ffi/Cif.h
  • NativeScript/ffi/Cif.mm
  • NativeScript/ffi/Class.mm
  • NativeScript/ffi/ClassBuilder.mm
  • NativeScript/ffi/ClassMember.h
  • NativeScript/ffi/ClassMember.mm
  • NativeScript/ffi/Closure.mm
  • NativeScript/ffi/EngineDirectCall.h
  • NativeScript/ffi/EngineDirectCall.mm
  • NativeScript/ffi/HermesFastCallbackInfo.h
  • NativeScript/ffi/HermesFastNativeApi.h
  • NativeScript/ffi/HermesFastNativeApi.mm
  • NativeScript/ffi/Interop.mm
  • NativeScript/ffi/JSCFastNativeApi.h
  • NativeScript/ffi/JSCFastNativeApi.mm
  • NativeScript/ffi/ObjCBridge.h
  • NativeScript/ffi/ObjCBridge.mm
  • NativeScript/ffi/Object.h
  • NativeScript/ffi/Object.mm
  • NativeScript/ffi/Protocol.mm
  • NativeScript/ffi/QuickJSFastNativeApi.h
  • NativeScript/ffi/QuickJSFastNativeApi.mm
  • NativeScript/ffi/SignatureDispatch.h
  • NativeScript/ffi/TypeConv.h
  • NativeScript/ffi/TypeConv.mm
  • NativeScript/ffi/V8FastNativeApi.h
  • NativeScript/ffi/V8FastNativeApi.mm
  • NativeScript/ffi/jsi/NativeApiJsi.h
  • NativeScript/ffi/jsi/NativeApiJsi.mm
  • NativeScript/ffi/jsi/NativeApiJsiReactNative.h
  • NativeScript/ffi/jsi/README.md
  • NativeScript/napi/hermes/jsr.cpp
  • NativeScript/napi/hermes/jsr.h
  • NativeScript/napi/jsc/jsc-api.cpp
  • NativeScript/napi/jsc/jsc-api.h
  • NativeScript/napi/quickjs/quickjs-api.c
  • NativeScript/napi/v8/v8-api.cpp
  • NativeScript/runtime/Runtime.cpp
  • NativeScript/runtime/modules/console/Console.cpp
  • NativeScript/runtime/modules/node/Process.cpp
  • NativeScript/runtime/modules/timers/Timers.mm
  • benchmarks/objc-dispatch/README.md
  • benchmarks/objc-dispatch/objc-dispatch-benchmarks.js
  • benchmarks/objc-dispatch/run.js
  • examples/expo-demo/App.tsx
  • examples/expo-demo/README.md
  • examples/expo-demo/app.config.js
  • examples/react-native-demo/App.tsx
  • examples/react-native-demo/README.md
  • metadata-generator/src/SignatureDispatchEmitter.cpp
  • package.json
  • packages/ios/package.json
  • packages/react-native/LICENSE
  • packages/react-native/NativeScriptNativeApi.podspec
  • packages/react-native/README.md
  • packages/react-native/app.plugin.js
  • packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.h
  • packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm
  • packages/react-native/ios/NativeScriptNativeApiModule.h
  • packages/react-native/ios/NativeScriptNativeApiModule.mm
  • packages/react-native/ios/NativeScriptNativeApiModuleProvider.h
  • packages/react-native/ios/NativeScriptNativeApiModuleProvider.mm
  • packages/react-native/ios/NativeScriptUIView.h
  • packages/react-native/ios/NativeScriptUIView.mm
  • packages/react-native/ios/NativeScriptUIViewManager.mm
  • packages/react-native/package.json
  • packages/react-native/plugin/withNativeScriptReactNative.js
  • packages/react-native/react-native.config.js
  • packages/react-native/src/NativeScriptNativeApi.ts
  • packages/react-native/src/NativeScriptUIViewNativeComponent.ts
  • packages/react-native/src/index.d.ts
  • packages/react-native/src/index.ts
  • scripts/build_all_ios.sh
  • scripts/build_all_macos.sh
  • scripts/build_all_node_api.sh
  • scripts/build_nativescript.sh
  • scripts/build_npm_ios.sh
  • scripts/build_react_native_turbomodule.sh
  • scripts/create_react_native_demo.sh
  • scripts/get-npm-tag.js
  • scripts/metagen.js
  • scripts/react_native_app_utils.sh
  • scripts/run-tests-macos.js
  • scripts/test_react_native_ffi_compat.sh
  • scripts/test_react_native_turbomodule.sh
  • test/react-native/ffi-compat/App.tsx
  • test/runtime/fixtures/Marshalling/TNSFunctionPointers.h
  • test/runtime/fixtures/Marshalling/TNSFunctionPointers.m
  • test/runtime/fixtures/exported-symbols.txt
  • test/runtime/runner/app/tests/ApiTests.js
  • test/runtime/runner/app/tests/Marshalling/FunctionPointerTests.js
  • test/runtime/runner/app/tests/NativeApiJsiTests.js
  • test/runtime/runner/app/tests/index.js
 ________________________________________
< CI is red. I'm also red. We match now. >
 ----------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/hermes-direct-layer-optimizations

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable sequence diagrams in the walkthrough.

Disable the reviews.sequence_diagrams setting to disable sequence diagrams in the walkthrough.

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