Skip to content

Commit

Permalink
[Swift] Improves vectors performance & arrays within lib (#8415)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mustiikhalil authored Nov 19, 2024
1 parent a9df448 commit 1f4a903
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 47 deletions.
19 changes: 9 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -508,17 +508,16 @@ jobs:
name: Build Swift Wasm
runs-on: ubuntu-24.04
container:
image: ghcr.io/swiftwasm/carton:0.15.3
image: ghcr.io/swiftwasm/carton:0.20.1
steps:
- uses: actions/checkout@v3
- name: Setup Wasmer
uses: wasmerio/setup-wasmer@v2
- uses: swiftwasm/setup-swiftwasm@v1
with:
swift-version: "wasm-5.9.2-RELEASE"
- name: Test
working-directory: tests/swift/Wasm.tests
run: swift run carton test
- uses: actions/checkout@v3
- uses: bytecodealliance/actions/wasmtime/setup@v1
- uses: swiftwasm/setup-swiftwasm@v1
with:
swift-version: "wasm-6.0.2-RELEASE"
- name: Test
working-directory: tests/swift/Wasm.tests
run: swift run carton test

build-ts:
name: Build TS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,24 @@ let benchmarks = {
let root = Offset(offset: fb.endTable(at: start))
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))
}
}
}
2 changes: 1 addition & 1 deletion benchmarks/swift/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let package = Package(
.package(path: "../.."),
.package(
url: "https://github.com/ordo-one/package-benchmark",
from: "1.12.0"),
from: "1.27.0"),
],
targets: [
.executableTarget(
Expand Down
30 changes: 17 additions & 13 deletions swift/Sources/FlatBuffers/ByteBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public struct ByteBuffer {
ensureSpace(size: ptr.count)
memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!),
ptr.baseAddress!,
ptr.count)
_writerSize = _writerSize &+ ptr.count
}
Expand All @@ -264,9 +264,10 @@ public struct ByteBuffer {
mutating func push<T: NativeStruct>(elements: [T]) {
elements.withUnsafeBytes { ptr in
ensureSpace(size: ptr.count)
_storage.memory
.advanced(by: writerIndex &- ptr.count)
.copyMemory(from: ptr.baseAddress!, byteCount: ptr.count)
memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count),
ptr.baseAddress!,
ptr.count)
_writerSize = _writerSize &+ ptr.count
}
}
Expand All @@ -281,7 +282,7 @@ public struct ByteBuffer {
ensureSpace(size: ptr.count)
memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!),
ptr.baseAddress!,
ptr.count)
_writerSize = _writerSize &+ ptr.count
}
Expand All @@ -296,11 +297,10 @@ public struct ByteBuffer {
@inline(__always)
mutating func push<T: NativeStruct>(struct value: T, size: Int) {
ensureSpace(size: size)
var v = value
withUnsafeBytes(of: &v) {
withUnsafePointer(to: value) {
memcpy(
_storage.memory.advanced(by: writerIndex &- size),
$0.baseAddress!,
$0,
size)
_writerSize = _writerSize &+ size
}
Expand All @@ -314,11 +314,10 @@ public struct ByteBuffer {
@usableFromInline
mutating func push<T: Scalar>(value: T, len: Int) {
ensureSpace(size: len)
var v = value
withUnsafeBytes(of: &v) {
withUnsafePointer(to: value) {
memcpy(
_storage.memory.advanced(by: writerIndex &- len),
$0.baseAddress!,
$0,
len)
_writerSize = _writerSize &+ len
}
Expand Down Expand Up @@ -355,7 +354,7 @@ public struct ByteBuffer {
{
memcpy(
_storage.memory.advanced(by: writerIndex &- len),
UnsafeRawPointer(bytes.baseAddress!),
bytes.baseAddress!,
len)
_writerSize = _writerSize &+ len
return true
Expand All @@ -377,7 +376,12 @@ public struct ByteBuffer {
}
assert(index < _storage.capacity, "Write index is out of writing bound")
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
Expand Down
38 changes: 16 additions & 22 deletions swift/Sources/FlatBuffers/FlatBufferBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ public struct FlatBufferBuilder {
/// by the generated code*
@inline(__always)
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 startTable = start &- Int(_bb.read(def: Int32.self, position: start))
let isOkay = _bb.read(
def: VOffset.self,
position: startTable &+ Int(field)) != 0
position: startTable &+ Int(fields[index])) != 0
assert(isOkay, "Flatbuffers requires the following field")
}
}
Expand Down Expand Up @@ -285,13 +285,13 @@ public struct FlatBufferBuilder {
let vt2 = _bb.memory.advanced(by: _bb.writerIndex)
let len2 = vt2.load(fromByteOffset: 0, as: Int16.self)

for table in _vtables {
let position = _bb.capacity &- Int(table)
for index in stride(from: 0, to: _vtables.count, by: 1) {
let position = _bb.capacity &- Int(_vtables[index])
let vt1 = _bb.memory.advanced(by: position)
let len1 = _bb.read(def: Int16.self, position: position)
if len2 != len1 || 0 != memcmp(vt1, vt2, Int(len2)) { continue }

isAlreadyAdded = Int(table)
isAlreadyAdded = Int(_vtables[index])
break
}

Expand Down Expand Up @@ -380,7 +380,7 @@ public struct FlatBufferBuilder {
@inline(__always)
@usableFromInline
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
Expand Down Expand Up @@ -524,8 +524,8 @@ public struct FlatBufferBuilder {
{
let size = size
startVector(size, elementSize: T.byteSize)
for e in elements.reversed() {
_bb.push(value: e.value, len: T.byteSize)
for index in stride(from: elements.count, to: 0, by: -1) {
_bb.push(value: elements[index &- 1].value, len: T.byteSize)
}
return endVector(len: size)
}
Expand Down Expand Up @@ -569,8 +569,8 @@ public struct FlatBufferBuilder {
len: Int) -> Offset
{
startVector(len, elementSize: MemoryLayout<Offset>.size)
for o in offsets.reversed() {
push(element: o)
for index in stride(from: offsets.count, to: 0, by: -1) {
push(element: offsets[index &- 1])
}
return endVector(len: len)
}
Expand All @@ -593,8 +593,8 @@ public struct FlatBufferBuilder {
@inline(__always)
mutating public func createVector(ofStrings str: [String]) -> Offset {
var offsets: [Offset] = []
for s in str {
offsets.append(create(string: s))
for index in stride(from: 0, to: str.count, by: 1) {
offsets.append(create(string: str[index]))
}
return createVector(ofOffsets: offsets)
}
Expand Down Expand Up @@ -646,9 +646,8 @@ public struct FlatBufferBuilder {
struct s: T, position: VOffset) -> Offset
{
let offset = create(struct: s)
_vtableStorage.add(loc: FieldLoc(
offset: _bb.size,
position: VOffset(position)))
_vtableStorage.add(
loc: (offset: _bb.size, position: VOffset(position)))
return offset
}

Expand Down Expand Up @@ -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
@usableFromInline
internal class VTableStorage {
Expand Down Expand Up @@ -920,12 +921,5 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
func load(at index: Int) -> FieldLoc {
memory.load(fromByteOffset: index, as: FieldLoc.self)
}

}

internal struct FieldLoc {
var offset: UOffset
var position: VOffset
}

}
1 change: 1 addition & 0 deletions tests/swift/Wasm.tests/.swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wasm-6.0.2-RELEASE
2 changes: 1 addition & 1 deletion tests/swift/Wasm.tests/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ let package = Package(
],
dependencies: [
.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: [
.target(name: "Wasm"),
Expand Down

0 comments on commit 1f4a903

Please sign in to comment.