mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 04:04:19 +00:00
[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:
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
tests/swift/Wasm.tests/.swift-version
Normal file
1
tests/swift/Wasm.tests/.swift-version
Normal file
@@ -0,0 +1 @@
|
||||
wasm-6.0.2-RELEASE
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user