GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — perf(runtime): drop per-call struct, size and swizzle lookups around ffi_call by edusperoni · Pull Request #511 · NativeScript/ios Conversation
|
| case | main | branch | delta |
|---|---|---|---|
struct return: view.frame |
274 | 232 | -16% |
screen.bounds |
279 | 229 | -18% |
screen.bounds.size.width |
515 | 428 | -17% |
UIScreen.mainScreen.bounds.size.width |
656 | 574 | -12% |
struct arg (plain object): view.frame = {...} |
717 | 550 | -23% |
struct arg (plain object): NSValue.valueWithCGRect({...}) |
1498 | 1263 | -16% |
struct arg (struct object): view.frame = rect |
205 | 198 | -3% |
struct arg (null): NSValue.valueWithCGRect(null) |
990 | 934 | -6% |
anonymous struct round trip (TNSTestNativeCallbacks.recordsNestedAnonymousStruct) |
1649 | 1281 | -22% |
NSError out-param omitted: api.methodError(0) |
559 | 536 | -4% |
block callback, per item: enumerateObjectsUsingBlock(noop) |
466 | 423 | -9% |
arr.count |
86 | 83 | -3% |
arr.indexOfObject(obj) |
122 | 118 | -4% |
obj.isKindOfClass(NSObject) |
196 | 190 | -3% |
view.setNeedsLayout() |
110 | 106 | -4% |
screen.nativeScale |
146 | 142 | -3% |
Plain instance calls gain only a few ns from skipping respondsToSelector:, since that probe hits the method cache. The fixture-based cases include the fixture's own TNSLog/stringWithFormat: work, so their absolute numbers overstate the runtime's share.
The bench specs and harness (Release build, a minimal runner replacing app/index.js, results read from the app container) live outside the tree, in bench/ of the main checkout, like the ones for #496.
./run_tests.sh (Debug, dedicated simulator): 1835 specs, 0 failures, 11 skipped../run_tests.sh -a (AddressSanitizer): 1835 specs, 0 failures, 16 skipped, no ASan reports.ArgConverter::Invoke): every instance call hashes object_getClassName(target) into ClassPrototypes and copies the Caches shared_ptr (atomic inc/dec). Caching this per Class pointer looks right, because a class is extended at creation. But ClassPrototypes is per isolate, and how HMR re-extension names and replaces classes needs checking first.CacheItem: ParametrizedCall::Get and MethodMeta::selector() are a spinlocked hash lookup each, on every call. Hoisting them needs plumbing through ObjCMethodCall/Invoke; perf(runtime): cache StructInfo, struct prototypes and the Class on the native call path #496 estimated this at ~5%.main. A per-selector generation stamp cached at the call site would make this lock-free. That only matters for apps that patch native prototypes.ffi_type per return (Interop::GetResult, ExtVector branch). The wrapper owns it, and ObjectManager disposes it. Sharing one per encoding means changing that ownership, so it's left alone here.MetadataBuilder struct branch) keys StructInfo by the native name, while returns and the cif key it by jsName. For a struct whose jsName differs, the two get distinct StructInfos, and so distinct prototypes and instanceof results. This predates this PR, which keeps the jsName side. Unifying the two is a behavior change.MethodCallback still copies the callback's shared_ptr and the Caches shared_ptr per invocation. Dropping those copies needs a lifetime argument: the wrapper or block can be disposed while the JS callback runs.Class returns (GetResult, ClassEncoding) call GetMeta and ObjectCtorInitializer per return. That path wasn't measured here.ffi_call itself with direct objc_msgSend stubs is feat(runtime): AOT direct calls — app-compiled objc_msgSend stubs bound in place of libffi #366.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…ffi_call - Resolve StructInfo once per struct TypeEncoding (FFICall::GetStructInfo overload keyed by encoding address) instead of rebuilding the name string, taking ArgConverter::GetMeta's mutex and hashing the name twice on every struct argument, return and nested field; anonymous structs no longer rebuild their stringstream key per call. - FFICall::GetTypeSize replaces the GetArgumentType + DisposeFFIType pairs that built (and, for arrays/vectors, heap-allocated) an ffi_type only to read its size: null/undefined arguments, by-reference out parameters, constant arrays. - Selectors the runtime swizzles are registered up front; method calls skip the respondsToSelector: probe for every other selector, and skip the registry lock entirely until the first swizzle. - The NSError out-parameter slot lives on the stack instead of calloc/free. - Closure callbacks collect up to 8 JS arguments in an inline array instead of a heap-allocated vector.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews: auto_review: drafts: true
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. Comment |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.