Skip to content

perf(shared-core): copy bytes across the bridge in one memcpy - #1503

Merged
bmc08gt merged 1 commit into
code/cashfrom
perf/shared-core-bulk-bytes
Sep 18, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
perf/shared-core-bulk-bytes

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Data.kotlinByteArray filled a KotlinByteArray with set(index:value:), and Data.init(_:) read one back with get(index:). Both are Objective-C message sends, so the cost is one send per byte.

That is invisible for a 32-byte key or a hash, which is every other caller of this bridge. It is not invisible for the discrete-curve tables. The iOS app hands the bridge 6,720,032 bytes the first time anything touches DiscreteBondingCurve, which happens inside StoredBalance.init while the session container is being built, on the main thread, before the app reaches its tabs. A Debug launch on an iPhone 17 Pro simulator spent 2849 ms of its 3047 ms inside SharedBondingCurve.initialize — 93% of everything between AppDelegate.init and the logged-in state. A Release launch measured earlier spent 1773 ms of 1957 ms, so the cost is bridge dispatch rather than unoptimised Swift.

The change

SharedBytes, in a new appleMain source set, converts through NSData using usePinned and memcpy, so each direction is a single call. Kotlin bytes are signed, but a bulk copy moves the bit patterns unchanged, so the hand-written reinterpretation in the old reverse copy is no longer needed.

A round trip of 6,720,032 bytes in SharedCoreKitTests goes from 1.013 s to 0.002 s. End to end, with FLIPCASH_SHARED_CORE_LOCAL pointed at this branch, the gap between the iOS app's completeLogin and the first controller built after the curve load collapses from about 3 seconds to under one.

Scope

Array<Int64>.kotlinLongArray has the same per-element shape and is left alone; its callers pass small arrays. If a bulk Int64 path is ever needed, SharedBytes is where it goes.

Publishing a release and bumping the iOS pin is a follow-up; nothing in the app repos changes here.

`Data.kotlinByteArray` filled a `KotlinByteArray` with `set(index:value:)`, and
`Data.init(_:)` read one back with `get(index:)`. Both are Objective-C message
sends, so the cost is one send per byte. That is invisible for a 32-byte key or
a hash, and it is not invisible for the discrete-curve tables: the iOS app hands
the bridge 6,720,032 bytes on first use of `DiscreteBondingCurve`, and spent
between 1.8s and 2.8s inside `SharedBondingCurve.initialize` doing it, on the
main thread during launch.

`SharedBytes` in a new `appleMain` source set converts through `NSData` with
`usePinned` and `memcpy`, so each direction is a single call. Kotlin bytes are
signed, but a bulk copy moves the bit patterns unchanged, so the reinterpretation
the old reverse copy did by hand is no longer needed.

A round trip of 6,720,032 bytes in `SharedCoreKitTests` goes from 1.013s to
0.002s. The existing round-trip tests, including the one over every byte value,
still pass, as do the Ed25519, Base58 and bonding-curve vector suites.

`Array<Int64>.kotlinLongArray` has the same per-element shape and is left alone;
its callers pass small arrays.
@bmc08gt bmc08gt self-assigned this Sep 18, 2026
@bmc08gt
bmc08gt merged commit ffa465f into code/cash Sep 18, 2026
4 checks passed
@bmc08gt
bmc08gt deleted the perf/shared-core-bulk-bytes branch September 23, 2026 16:45
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