diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift index 60d632d1b97..c72241b214a 100644 --- a/swift/Sources/FlatBuffers/ByteBuffer.swift +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -238,6 +238,20 @@ public struct ByteBuffer { } } + /// Adds an array of type Scalar to the buffer memory + /// - Parameter elements: An array of Scalars + @inline(__always) + @usableFromInline + mutating func push(elements: [T]) { + elements.withUnsafeBytes { ptr in + ensureSpace(size: ptr.count) + _storage.memory + .advanced(by: writerIndex &- ptr.count) + .copyMemory(from: ptr.baseAddress!, byteCount: ptr.count) + self._writerSize = self._writerSize &+ ptr.count + } + } + /// Adds a `ContiguousBytes` to buffer memory /// - Parameter value: bytes to copy #if swift(>=5.0) && !os(WASI) @@ -248,8 +262,8 @@ public struct ByteBuffer { ensureSpace(size: ptr.count) memcpy( _storage.memory.advanced(by: writerIndex &- ptr.count), - UnsafeRawPointer(ptr.baseAddress!), - ptr.count) + UnsafeRawPointer(ptr.baseAddress!), + ptr.count) self._writerSize = self._writerSize &+ ptr.count } } diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift index 1fb66d55a0c..bf1978e3347 100644 --- a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -623,9 +623,7 @@ public struct FlatBufferBuilder { startVector( structs.count * MemoryLayout.size, elementSize: MemoryLayout.alignment) - for i in structs.reversed() { - _ = create(struct: i) - } + _bb.push(elements: structs) return endVector(len: structs.count) } diff --git a/swift/Sources/FlatBuffers/Verifiable.swift b/swift/Sources/FlatBuffers/Verifiable.swift index 09f1a354db3..f16d42172b5 100644 --- a/swift/Sources/FlatBuffers/Verifiable.swift +++ b/swift/Sources/FlatBuffers/Verifiable.swift @@ -129,7 +129,9 @@ public enum Vector: Verifiable where U: Verifiable, S: Verifiable { let range = try verifyRange(&verifier, at: position, of: UOffset.self) for index in stride( from: range.start, - to: Int(clamping: range.start &+ (range.count &* MemoryLayout.size)), + to: Int( + clamping: range + .start &+ (range.count &* MemoryLayout.size)), by: MemoryLayout.size) { try U.verify(&verifier, at: index, of: U.self) diff --git a/tests/swift/benchmarks/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift b/tests/swift/benchmarks/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift new file mode 100644 index 00000000000..0181c425144 --- /dev/null +++ b/tests/swift/benchmarks/Benchmarks/FlatbuffersBenchmarks/FlatbuffersBenchmarks.swift @@ -0,0 +1,154 @@ +///* +// * Copyright 2023 Google Inc. All rights reserved. +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +import Benchmark +import CoreFoundation +import FlatBuffers + +@usableFromInline +struct AA: NativeStruct { + public init(a: Double, b: Double) { + self.a = a + self.b = b + } + var a: Double + var b: Double +} + +let benchmarks = { + let ints: [Int] = Array(repeating: 42, count: 100) + let bytes: [UInt8] = Array(repeating: 42, count: 100) + let str = (0...99).map { _ -> String in "x" }.joined() + let array: [AA] = [ + AA(a: 2.4, b: 2.4), + AA(a: 2.4, b: 2.4), + AA(a: 2.4, b: 2.4), + AA(a: 2.4, b: 2.4), + AA(a: 2.4, b: 2.4), + ] + + Benchmark("Allocating") { benchmark in + for i in benchmark.scaledIterations { + var fb = FlatBufferBuilder(initialSize: 1_024_000_000) + } + } + + Benchmark("Strings 10") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1<<20) + for i in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + _ = fb.create(string: "foobarbaz") + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("Strings 100") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1<<20) + for i in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + _ = fb.create(string: str) + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("Vector 100 Ints") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1<<20) + benchmark.startMeasurement() + for i in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + _ = fb.createVector(ints) + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("Vector 100 Bytes") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1<<20) + for i in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + _ = fb.createVector(bytes) + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("Vector 100 ContiguousBytes") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1<<20) + for i in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + _ = fb.createVector(bytes: bytes) + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("FlatBufferBuilder Add") { benchmark in + var fb = FlatBufferBuilder(initialSize: 1024 * 1024 * 32) + for _ in benchmark.scaledIterations { + benchmark.startMeasurement() + for _ in 0..<1_000_000 { + let off = fb.create(string: "T") + let s = fb.startTable(with: 4) + fb.add(element: 3.2, def: 0, at: 2) + fb.add(element: 4.2, def: 0, at: 4) + fb.add(element: 5.2, def: 0, at: 6) + fb.add(offset: off, at: 8) + _ = fb.endTable(at: s) + } + benchmark.stopMeasurement() + fb.clear() + } + } + + Benchmark("structs") { benchmark in + let structCount = 1_000_000 + + let rawSize = ((16 * 5) * structCount) / 1024 + + var fb = FlatBufferBuilder(initialSize: Int32(rawSize * 1600)) + + for _ in benchmark.scaledIterations { + benchmark.startMeasurement() + var offsets: [Offset] = [] + for _ in 0...size, - elementSize: MemoryLayout.alignment) - for _ in 0..<5 { - _ = fb.create(struct: AA(a: 2.4, b: 2.4)) - } - let vector = fb.endVector(len: 5) - let start = fb.startTable(with: 1) - fb.add(offset: vector, at: 4) - offsets.append(Offset(offset: fb.endTable(at: start))) - } - let vector = fb.createVector(ofOffsets: offsets) - let start = fb.startTable(with: 1) - fb.add(offset: vector, at: 4) - let root = Offset(offset: fb.endTable(at: start)) - fb.finish(offset: root) -} - -let str = (0...99).map { _ -> String in "x" }.joined() -let bytes: [UInt8] = Array(repeating: 42, count: 100) - -@usableFromInline -struct AA: NativeStruct { - public init(a: Double, b: Double) { - self.a = a - self.b = b - } - var a: Double - var b: Double -} - -Benchmark.main() diff --git a/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersNanInfTests.swift b/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersNanInfTests.swift index 96b5614e66a..07cdeb0105a 100644 --- a/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersNanInfTests.swift +++ b/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersNanInfTests.swift @@ -67,9 +67,9 @@ final class FlatBuffersNanInfTests: XCTestCase { let data = try encoder.encode(reader) let decoder = JSONDecoder() decoder.nonConformingFloatDecodingStrategy = .convertFromString( - positiveInfinity: "inf", - negativeInfinity: "-inf", - nan: "nan") + positiveInfinity: "inf", + negativeInfinity: "-inf", + nan: "nan") decoder.keyDecodingStrategy = .convertFromSnakeCase let value = try decoder.decode(Test.self, from: data) XCTAssertEqual(value.value, 100) diff --git a/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift b/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift index 61dbe50ab7f..be9b405e0fd 100644 --- a/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift +++ b/tests/swift/tests/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift @@ -53,6 +53,26 @@ final class FlatBuffersVectors: XCTestCase { // swiftformat:enable all } + func testCreateStructArray() { + struct Vec: NativeStruct { + let x, y, z: Float32 + } + let vector: [Vec] = [ + Vec(x: 1, y: 2, z: 3), + Vec(x: 4, y: 5, z: 6), + Vec(x: 7, y: 8, z: 9), + ] + var b = FlatBufferBuilder(initialSize: 100) + let o = b.createVector(ofStructs: vector) + b.finish(offset: o) + vector.withUnsafeBytes { pointer in + print(Array(pointer)) + } + // swiftformat:disable all + XCTAssertEqual(b.sizedByteArray, [4, 0, 0, 0, 3, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64, 0, 0, 160, 64, 0, 0, 192, 64, 0, 0, 224, 64, 0, 0, 0, 65, 0, 0, 16, 65]) + // swiftformat:enable all + } + func testCreateEmptyIntArray() { let numbers: [Int32] = [] var b = FlatBufferBuilder(initialSize: 20)