◐ Shell
clean mode source ↗

BridgeJS: Optimize numeric array transfer with bulk TypedArray copy by krodak · Pull Request #745 · swiftwasm/JavaScriptKit

Overview

Optimize numeric array transfer by using bulk TypedArray memory copy instead of element-by-element stack serialization. When a Swift function passes or receives a numeric array ([Int], [UInt8], [Float], [Double], etc.), BridgeJS now transfers the data as a single bulk operation. TypeScript types remain number[] / bigint[] — no user-facing API change.

This is a transparent performance optimization following the same pattern as bridgeJSStackPopAsOptional — a specialized ABI for numeric element types without changing the type contract. Non-numeric arrays ([String], [MyStruct], etc.) continue using the existing element-by-element stack protocol.

1. Swift → JS direction: Array.bridgeJSTypedArrayPush() calls withUnsafeBufferPointer to pass (ptr, count, kind) to a new swift_js_push_typed_array WASM import, which copies the bytes into a JS TypedArray. A pre-allocated for-loop then converts to number[] for the caller.

2. JS → Swift direction: [T].bridgeJSTypedArrayLiftParameter(sourceId, count) receives a retained TypedArray from JS, allocates via Array(unsafeUninitializedCapacity:), and calls back to JS via swift_js_init_typed_array_memory to bulk copy — the same retain-allocate-callback pattern used by string lowering.

3. Affected types: All integer types (Int8 through Int64, signed and unsigned), Float, Double, plus pointer-width Int/UInt. A new _BridgeJSTypedArrayElement protocol marks eligible types. BridgeType.isNumericScalar and typedArrayKind on the skeleton side route the codegen.

Benchmark results

Release build, 10K iterations × 5 runs:

Test Before After Speedup
takeIntArray (1K, JS→Swift) 758 ms 13 ms 58×
takeDoubleArray (1K, JS→Swift) 791 ms 13 ms 61×
makeIntArray (1K, Swift→JS) 221 ms 18 ms 12×
makeIntArrayLarge (10K, Swift→JS) 2160 ms 146 ms 15×
roundtripIntArray (1K) 964 ms 29 ms 33×
takeStringArray (non-numeric) 23 ms 25 ms unchanged