Benchmarking methodology
The library includes a built-in benchmark suite (BenchmarkScreen.tsx:1-352) that compares Nitro Modules against the traditional bridge-based react-native-version-check.
Test configuration
- Performs 1,000 warmup iterations to stabilize JIT compilation
- Runs 100,000 iterations per test
- Repeats 5 times and averages the results
- Uses
performance.now()for high-resolution timing
Benchmarks are included in the example app. Run
npm run example ios or npm run example android to test on your device.Real-world performance
Scenario: Get all version info
A realistic use case is reading all app metadata at startup:BenchmarkScreen.tsx:72-101):
| Platform | Nitro | Bridge | Speedup |
|---|---|---|---|
| iOS | ~12ms | ~2,800ms | 233x faster |
| Android | ~15ms | ~3,200ms | 213x faster |
These are averaged results from 5 runs of 100,000 iterations on physical devices. Your results may vary based on device performance.
Why the massive speedup?
Nitro approach:- 3 property reads: JavaScript variable access (already cached at init)
- 1 JSI call: Direct C++ → Swift/Kotlin (no serialization)
- 4 async calls: JavaScript → JSON → Native Queue → Response Queue → JSON → JavaScript
- Each call requires Promise creation, queueing, and microtask scheduling
Individual method breakdown
Property reads
| Method | Nitro | Bridge | Speedup |
|---|---|---|---|
packageName | ~0.8ms | ~650ms | 812x |
version | ~0.7ms | ~640ms | 914x |
buildNumber | ~0.8ms | ~655ms | 819x |
Synchronous JSI call
| Platform | Nitro | Bridge | Speedup |
|---|---|---|---|
| iOS | ~1.2ms | ~850ms | 708x |
| Android | ~1.5ms | ~920ms | 613x |
Asynchronous methods
Network operations likegetLatestVersion() are async in both implementations:
- Bridge serialization overhead
- JavaScript → Native queue overhead
- Promise wrapping overhead
Zero-overhead cached properties
How caching works
At module initialization (index.ts:8-12):
- Read once via JSI when the module loads
- Stored as plain JavaScript strings
- Exported directly from the module
Subsequent reads
Every time you accessVersionCheck.version, you’re reading a JavaScript string — no native call happens.
Memory overhead
Each property is a JavaScript string. Typical sizes:| Property | Example | Size |
|---|---|---|
version | "1.2.3" | ~10 bytes |
buildNumber | "42" | ~6 bytes |
packageName | "com.example.app" | ~20 bytes |
installSource | "appstore" | ~12 bytes |
Comparison to bridge-based approach
Traditional bridge (TurboModules)
Nitro Modules (JSI)
Why is JSI so much faster?
Why is JSI so much faster?
No serialization
- Bridge: JavaScript objects → JSON → Native objects
- JSI: Shared memory pointers (zero-copy)
- Bridge: Calls go through a single-threaded message queue
- JSI: Direct function calls
- Bridge: Always async (Promise overhead)
- JSI: Can be synchronous for simple operations
- Bridge: Runtime type checks and conversions
- JSI: Compile-time type safety (C++ templates)
Memory footprint
Module initialization
| Component | Size |
|---|---|
| C++ Hybrid Object | ~200 bytes |
| Cached JS strings (4) | ~50 bytes |
| Native class instance (Swift/Kotlin) | ~100 bytes |
| URLSession/HttpURLConnection | ~500 bytes |
Runtime allocations
Property reads: Zero allocations (cached values)getCountry():
- Returns existing
Localevalue - Allocation: ~10 bytes (string)
getLatestVersion():
- Network request + JSON parsing
- Allocation: ~2-5 KB (response data, temporary objects)
- Freed after Promise resolves
When to use sync vs async methods
Use synchronous access for:
-
Static app metadata (always use properties):
-
Device locale (fast, no I/O):
Use asynchronous access for:
-
Network requests (always async):
-
Operations that might block (none in this library, but good practice):
- File I/O
- Database queries
- Complex computations
Rule of thumb: If it can be cached or computed in < 0.1ms, make it synchronous. If it requires I/O or takes > 1ms, make it async.
Performance best practices
1. Cache properties at app startup
2. Avoid unnecessary async calls
3. Use needsUpdate() efficiently
4. Batch operations
Key takeaways
600-900x faster
Property reads via JSI are orders of magnitude faster than bridge calls
< 1 KB footprint
Entire module uses less than 1 KB of memory
Zero overhead
Cached properties have no native call cost after initialization
Sync > Async
Use synchronous APIs when possible to avoid Promise overhead