mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-27 17:38:06 +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
|
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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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: [
|
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"),
|
||||||
|
|||||||
Reference in New Issue
Block a user