[Swift] Improves vectors performance & arrays within lib (#8415)

* Improves vectors performance and adds a benchmark to vectors of offsets in swift

Improves performance for all arrays and for loops

Uses a tuple instead of allocating a struct each time we start iterating over fieldloc

Updates benchmark library

* Fixing swift Wasm ci
This commit is contained in:
mustiikhalil
2024-11-19 07:02:47 +01:00
committed by GitHub
parent a9df44828d
commit 1f4a9038ce
7 changed files with 65 additions and 47 deletions

View File

@@ -508,17 +508,16 @@ jobs:
name: Build Swift Wasm name: Build Swift Wasm
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
container: container:
image: ghcr.io/swiftwasm/carton:0.15.3 image: ghcr.io/swiftwasm/carton:0.20.1
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Wasmer - uses: bytecodealliance/actions/wasmtime/setup@v1
uses: wasmerio/setup-wasmer@v2 - uses: swiftwasm/setup-swiftwasm@v1
- uses: swiftwasm/setup-swiftwasm@v1 with:
with: swift-version: "wasm-6.0.2-RELEASE"
swift-version: "wasm-5.9.2-RELEASE" - name: Test
- name: Test working-directory: tests/swift/Wasm.tests
working-directory: tests/swift/Wasm.tests run: swift run carton test
run: swift run carton test
build-ts: build-ts:
name: Build TS name: Build TS

View File

@@ -178,4 +178,24 @@ let benchmarks = {
let root = Offset(offset: fb.endTable(at: start)) let root = Offset(offset: fb.endTable(at: start))
fb.finish(offset: root) fb.finish(offset: root)
} }
Benchmark("Vector of Offsets") { benchmark in
let rawSize = ((16 * 5) * benchmark.scaledIterations.count) / 1024
var fb = FlatBufferBuilder(initialSize: Int32(rawSize * 1600))
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
let offsets = [
fb.create(string: "T"),
fb.create(string: "2"),
fb.create(string: "3"),
]
let off = fb.createVector(ofOffsets: [
fb.createVector(ofOffsets: offsets),
fb.createVector(ofOffsets: offsets),
])
let s = fb.startTable(with: 2)
fb.add(offset: off, at: 2)
blackHole(fb.endTable(at: s))
}
}
} }

View File

@@ -26,7 +26,7 @@ let package = Package(
.package(path: "../.."), .package(path: "../.."),
.package( .package(
url: "https://github.com/ordo-one/package-benchmark", url: "https://github.com/ordo-one/package-benchmark",
from: "1.12.0"), from: "1.27.0"),
], ],
targets: [ targets: [
.executableTarget( .executableTarget(

View File

@@ -251,7 +251,7 @@ public struct ByteBuffer {
ensureSpace(size: ptr.count) ensureSpace(size: ptr.count)
memcpy( memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count), _storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!), ptr.baseAddress!,
ptr.count) ptr.count)
_writerSize = _writerSize &+ ptr.count _writerSize = _writerSize &+ ptr.count
} }
@@ -264,9 +264,10 @@ public struct ByteBuffer {
mutating func push<T: NativeStruct>(elements: [T]) { mutating func push<T: NativeStruct>(elements: [T]) {
elements.withUnsafeBytes { ptr in elements.withUnsafeBytes { ptr in
ensureSpace(size: ptr.count) ensureSpace(size: ptr.count)
_storage.memory memcpy(
.advanced(by: writerIndex &- ptr.count) _storage.memory.advanced(by: writerIndex &- ptr.count),
.copyMemory(from: ptr.baseAddress!, byteCount: ptr.count) ptr.baseAddress!,
ptr.count)
_writerSize = _writerSize &+ ptr.count _writerSize = _writerSize &+ ptr.count
} }
} }
@@ -281,7 +282,7 @@ public struct ByteBuffer {
ensureSpace(size: ptr.count) ensureSpace(size: ptr.count)
memcpy( memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count), _storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!), ptr.baseAddress!,
ptr.count) ptr.count)
_writerSize = _writerSize &+ ptr.count _writerSize = _writerSize &+ ptr.count
} }
@@ -296,11 +297,10 @@ public struct ByteBuffer {
@inline(__always) @inline(__always)
mutating func push<T: NativeStruct>(struct value: T, size: Int) { mutating func push<T: NativeStruct>(struct value: T, size: Int) {
ensureSpace(size: size) ensureSpace(size: size)
var v = value withUnsafePointer(to: value) {
withUnsafeBytes(of: &v) {
memcpy( memcpy(
_storage.memory.advanced(by: writerIndex &- size), _storage.memory.advanced(by: writerIndex &- size),
$0.baseAddress!, $0,
size) size)
_writerSize = _writerSize &+ size _writerSize = _writerSize &+ size
} }
@@ -314,11 +314,10 @@ public struct ByteBuffer {
@usableFromInline @usableFromInline
mutating func push<T: Scalar>(value: T, len: Int) { mutating func push<T: Scalar>(value: T, len: Int) {
ensureSpace(size: len) ensureSpace(size: len)
var v = value withUnsafePointer(to: value) {
withUnsafeBytes(of: &v) {
memcpy( memcpy(
_storage.memory.advanced(by: writerIndex &- len), _storage.memory.advanced(by: writerIndex &- len),
$0.baseAddress!, $0,
len) len)
_writerSize = _writerSize &+ len _writerSize = _writerSize &+ len
} }
@@ -355,7 +354,7 @@ public struct ByteBuffer {
{ {
memcpy( memcpy(
_storage.memory.advanced(by: writerIndex &- len), _storage.memory.advanced(by: writerIndex &- len),
UnsafeRawPointer(bytes.baseAddress!), bytes.baseAddress!,
len) len)
_writerSize = _writerSize &+ len _writerSize = _writerSize &+ len
return true return true
@@ -377,7 +376,12 @@ public struct ByteBuffer {
} }
assert(index < _storage.capacity, "Write index is out of writing bound") assert(index < _storage.capacity, "Write index is out of writing bound")
assert(index >= 0, "Writer index should be above zero") assert(index >= 0, "Writer index should be above zero")
_storage.memory.storeBytes(of: value, toByteOffset: index, as: T.self) withUnsafePointer(to: value) {
memcpy(
_storage.memory.advanced(by: index),
$0,
MemoryLayout<T>.size)
}
} }
/// Makes sure that buffer has enouch space for each of the objects that will be written into it /// Makes sure that buffer has enouch space for each of the objects that will be written into it

View File

@@ -146,12 +146,12 @@ public struct FlatBufferBuilder {
/// by the generated code* /// by the generated code*
@inline(__always) @inline(__always)
mutating public func require(table: Offset, fields: [Int32]) { mutating public func require(table: Offset, fields: [Int32]) {
for field in fields { for index in stride(from: 0, to: fields.count, by: 1) {
let start = _bb.capacity &- Int(table.o) let start = _bb.capacity &- Int(table.o)
let startTable = start &- Int(_bb.read(def: Int32.self, position: start)) let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
let isOkay = _bb.read( let isOkay = _bb.read(
def: VOffset.self, def: VOffset.self,
position: startTable &+ Int(field)) != 0 position: startTable &+ Int(fields[index])) != 0
assert(isOkay, "Flatbuffers requires the following field") assert(isOkay, "Flatbuffers requires the following field")
} }
} }
@@ -285,13 +285,13 @@ public struct FlatBufferBuilder {
let vt2 = _bb.memory.advanced(by: _bb.writerIndex) let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
let len2 = vt2.load(fromByteOffset: 0, as: Int16.self) let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)
for table in _vtables { for index in stride(from: 0, to: _vtables.count, by: 1) {
let position = _bb.capacity &- Int(table) let position = _bb.capacity &- Int(_vtables[index])
let vt1 = _bb.memory.advanced(by: position) let vt1 = _bb.memory.advanced(by: position)
let len1 = _bb.read(def: Int16.self, position: position) let len1 = _bb.read(def: Int16.self, position: position)
if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue } if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue }
isAlreadyAdded = Int(table) isAlreadyAdded = Int(_vtables[index])
break break
} }
@@ -380,7 +380,7 @@ public struct FlatBufferBuilder {
@inline(__always) @inline(__always)
@usableFromInline @usableFromInline
mutating internal func track(offset: UOffset, at position: VOffset) { mutating internal func track(offset: UOffset, at position: VOffset) {
_vtableStorage.add(loc: FieldLoc(offset: offset, position: position)) _vtableStorage.add(loc: (offset: offset, position: position))
} }
// MARK: - Inserting Vectors // MARK: - Inserting Vectors
@@ -524,8 +524,8 @@ public struct FlatBufferBuilder {
{ {
let size = size let size = size
startVector(size, elementSize: T.byteSize) startVector(size, elementSize: T.byteSize)
for e in elements.reversed() { for index in stride(from: elements.count, to: 0, by: -1) {
_bb.push(value: e.value, len: T.byteSize) _bb.push(value: elements[index &- 1].value, len: T.byteSize)
} }
return endVector(len: size) return endVector(len: size)
} }
@@ -569,8 +569,8 @@ public struct FlatBufferBuilder {
len: Int) -> Offset len: Int) -> Offset
{ {
startVector(len, elementSize: MemoryLayout<Offset>.size) startVector(len, elementSize: MemoryLayout<Offset>.size)
for o in offsets.reversed() { for index in stride(from: offsets.count, to: 0, by: -1) {
push(element: o) push(element: offsets[index &- 1])
} }
return endVector(len: len) return endVector(len: len)
} }
@@ -593,8 +593,8 @@ public struct FlatBufferBuilder {
@inline(__always) @inline(__always)
mutating public func createVector(ofStrings str: [String]) -> Offset { mutating public func createVector(ofStrings str: [String]) -> Offset {
var offsets: [Offset] = [] var offsets: [Offset] = []
for s in str { for index in stride(from: 0, to: str.count, by: 1) {
offsets.append(create(string: s)) offsets.append(create(string: str[index]))
} }
return createVector(ofOffsets: offsets) return createVector(ofOffsets: offsets)
} }
@@ -646,9 +646,8 @@ public struct FlatBufferBuilder {
struct s: T, position: VOffset) -> Offset struct s: T, position: VOffset) -> Offset
{ {
let offset = create(struct: s) let offset = create(struct: s)
_vtableStorage.add(loc: FieldLoc( _vtableStorage.add(
offset: _bb.size, loc: (offset: _bb.size, position: VOffset(position)))
position: VOffset(position)))
return offset return offset
} }
@@ -837,6 +836,8 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
""" """
} }
typealias FieldLoc = (offset: UOffset, position: VOffset)
/// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer /// VTableStorage is a class to contain the VTable buffer that would be serialized into buffer
@usableFromInline @usableFromInline
internal class VTableStorage { internal class VTableStorage {
@@ -920,12 +921,5 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
func load(at index: Int) -> FieldLoc { func load(at index: Int) -> FieldLoc {
memory.load(fromByteOffset: index, as: FieldLoc.self) memory.load(fromByteOffset: index, as: FieldLoc.self)
} }
} }
internal struct FieldLoc {
var offset: UOffset
var position: VOffset
}
} }

View File

@@ -0,0 +1 @@
wasm-6.0.2-RELEASE

View File

@@ -24,7 +24,7 @@ let package = Package(
], ],
dependencies: [ dependencies: [
.package(path: "../../.."), .package(path: "../../.."),
.package(url: "https://github.com/swiftwasm/carton", exact: "1.0.1"), .package(url: "https://github.com/swiftwasm/carton", exact: "1.1.2"),
], ],
targets: [ targets: [
.target(name: "Wasm"), .target(name: "Wasm"),