swift 6.0+ performance tweaks 3x-6x (#9067)

* start/stop measurements

* tweaks

* add 6.0 annotations

* remove some inlines

* address feedback

* address wasi + let

* adopt flex buffers

* flex buffers benchmarks
This commit is contained in:
blindspot
2026-06-18 15:23:53 +02:00
committed by GitHub
parent 5982eb6495
commit 81edeb17d9
12 changed files with 288 additions and 27 deletions

View File

@@ -28,7 +28,7 @@ public let FileIdLength = 4
/// Protocol that All Scalars should conform to
///
/// Scalar is used to conform all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer.
public protocol Scalar: Equatable {
public protocol Scalar: Equatable, BitwiseCopyable {
associatedtype NumericValue
var convertedEndian: NumericValue { get }
}

View File

@@ -27,7 +27,7 @@ public struct ByteBuffer: @unchecked Sendable {
@usableFromInline
final class Storage {
@usableFromInline
enum Blob {
@frozen enum Blob: ~Copyable {
#if !os(WASI)
case data(Data)
case bytes(any ContiguousBytes)
@@ -36,6 +36,40 @@ public struct ByteBuffer: @unchecked Sendable {
case byteBuffer(_InternalByteBuffer)
case array([UInt8])
case pointer(UnsafeMutableRawPointer)
init(_ other: borrowing Blob) {
switch other {
#if !os(WASI)
case .data(let data):
self = .data(data)
case .bytes(let contiguousBytes):
self = .bytes(contiguousBytes)
#endif
case .byteBuffer(let internalByteBuffer):
self = .byteBuffer(internalByteBuffer)
case .array(let array):
self = .array(array)
case .pointer(let unsafeMutableRawPointer):
self = .pointer(unsafeMutableRawPointer)
}
}
var description: String {
switch self {
#if !os(WASI)
case .data(let data):
"data: \(data)"
case .bytes(let contiguousBytes):
"bytes: \(contiguousBytes)"
#endif
case .byteBuffer(let internalByteBuffer):
"byteBuffer: \(internalByteBuffer)"
case .array(let array):
"array: \(array)"
case .pointer(let unsafeMutableRawPointer):
"pointer: \(unsafeMutableRawPointer)"
}
}
}
/// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
@@ -44,7 +78,7 @@ public struct ByteBuffer: @unchecked Sendable {
private let capacity: Int
/// Retained blob of data that requires the storage to retain a pointer to.
@usableFromInline
var retainedBlob: Blob
let retainedBlob: Blob
@usableFromInline
init(count: Int) {
@@ -57,9 +91,9 @@ public struct ByteBuffer: @unchecked Sendable {
}
@usableFromInline
init(blob: Blob, capacity count: Int) {
init(blob: borrowing Blob, capacity count: Int) {
capacity = count
retainedBlob = blob
retainedBlob = .init(blob)
isOwned = false
}
@@ -150,6 +184,7 @@ public struct ByteBuffer: @unchecked Sendable {
@discardableResult
@inline(__always)
@inlinable
func readWithUnsafeRawPointer<T>(
position: Int,
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
@@ -278,7 +313,7 @@ public struct ByteBuffer: @unchecked Sendable {
/// - removeBytes: Removes a number of bytes from the current size
@inline(__always)
init(
blob: Storage.Blob,
blob: borrowing Storage.Blob,
count: Int,
removing removeBytes: Int)
{
@@ -318,7 +353,8 @@ public struct ByteBuffer: @unchecked Sendable {
/// - def: Type of the object
/// - position: the index of the object in the buffer
@inline(__always)
public func read<T>(def: T.Type, position: Int) -> T {
@inlinable
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
_storage.readWithUnsafeRawPointer(position: position) {
$0.bindMemory(to: T.self, capacity: 1)
.pointee
@@ -412,7 +448,7 @@ public struct ByteBuffer: @unchecked Sendable {
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
@inline(__always)
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
assert(removeBytes > 0, "Can NOT remove negative bytes")
assert(removeBytes >= 0, "Can NOT remove negative bytes")
assert(
removeBytes < capacity,
"Can NOT remove more bytes than the ones allocated")
@@ -464,8 +500,9 @@ public struct ByteBuffer: @unchecked Sendable {
extension ByteBuffer: CustomDebugStringConvertible {
public var debugDescription: String {
"""
buffer located at: \(_storage.retainedBlob),
let blobDescription = _storage.retainedBlob.description
return """
buffer located at: \(blobDescription),
with capacity of \(capacity),
{ writtenSize: \(_readerIndex), readerSize: \(reader),
size: \(size) }

View File

@@ -47,7 +47,8 @@ public struct FlatBufferBuilder {
/// A check to see if finish(::) was ever called to retreive data object
private(set) var finished = false
/// A check to see if the buffer should serialize Default values
private var serializeDefaults: Bool
@usableFromInline
let serializeDefaults: Bool
/// Current alignment for the buffer
var _minAlignment: Int = 0 {
@@ -756,6 +757,7 @@ public struct FlatBufferBuilder {
/// - offset: ``Offset`` of another object to be written
/// - position: The predefined position of the object
@inline(__always)
@inlinable
mutating public func add(offset: Offset, at position: VOffset) {
if offset.isEmpty { return }
add(element: refer(to: offset.o), def: 0, at: position)
@@ -794,6 +796,7 @@ public struct FlatBufferBuilder {
/// - def: Default value for that element
/// - position: The predefined position of the element
@inline(__always)
@inlinable
mutating public func add<T: Scalar>(
element: T,
def: T,
@@ -813,6 +816,7 @@ public struct FlatBufferBuilder {
/// - element: Optional element of type scalar
/// - position: The predefined position of the element
@inline(__always)
@inlinable
mutating public func add<T: Scalar>(element: T?, at position: VOffset) {
guard let element = element else { return }
track(offset: push(element: element), at: position)
@@ -825,6 +829,7 @@ public struct FlatBufferBuilder {
/// - Parameter element: Element to insert
/// - returns: position of the Element
@inline(__always)
@inlinable
@discardableResult
mutating public func push<T: Scalar>(element: T) -> UOffset {
let size = MemoryLayout<T>.size
@@ -836,7 +841,8 @@ public struct FlatBufferBuilder {
}
@inline(__always)
public func read<T>(def: T.Type, position: Int) -> T {
@inlinable
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
_bb.read(def: def, position: position)
}
}

View File

@@ -18,7 +18,7 @@ import Foundation
/// NativeStruct is a protocol that indicates if the struct is a native `swift` struct
/// since now we will be serializing native structs into the buffer.
public protocol NativeStruct {}
public protocol NativeStruct: BitwiseCopyable {}
public protocol FlatBufferVerifiableNativeStruct: NativeStruct, Verifiable {}

View File

@@ -82,7 +82,7 @@ public struct Table {
/// - Parameters:
/// - type: Type of Element that needs to be read from the buffer
/// - o: Offset of the Element
public func readBuffer<T>(of type: T.Type, at o: Int32) -> T {
public func readBuffer<T: BitwiseCopyable>(of type: T.Type, at o: Int32) -> T {
bb.read(def: T.self, position: Int(o &+ position))
}

View File

@@ -163,7 +163,7 @@ public struct Verifier {
/// - Parameter position: Current position to be read
/// - Throws: `inBuffer` errors
/// - Returns: a value of type `T` usually a `VTable` or a table offset
internal func getValue<T>(at position: Int) throws -> T {
internal func getValue<T: BitwiseCopyable>(at position: Int) throws -> T {
try inBuffer(position: position, of: T.self)
return _buffer.read(def: T.self, position: position)
}

View File

@@ -30,7 +30,7 @@ struct _InternalByteBuffer {
@usableFromInline
final class Storage {
/// pointer to the start of the buffer object in memory
private(set) var memory: UnsafeMutableRawPointer
@exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer
@usableFromInline
init(count: Int, alignment: Int) {
@@ -333,6 +333,7 @@ struct _InternalByteBuffer {
@discardableResult
@inline(__always)
@usableFromInline
func readWithUnsafeRawPointer<T>(
position: Int,
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T

View File

@@ -27,7 +27,7 @@ public struct ByteBuffer {
@usableFromInline
final class Storage {
@usableFromInline
enum Blob {
@frozen enum Blob: ~Copyable {
#if !os(WASI)
case data(Data)
case bytes(any ContiguousBytes)
@@ -36,6 +36,23 @@ public struct ByteBuffer {
case byteBuffer(_InternalByteBuffer)
case array([UInt8])
case pointer(UnsafeMutableRawPointer)
init(_ other: borrowing Blob) {
switch other {
#if !os(WASI)
case .data(let data):
self = .data(data)
case .bytes(let contiguousBytes):
self = .bytes(contiguousBytes)
#endif
case .byteBuffer(let internalByteBuffer):
self = .byteBuffer(internalByteBuffer)
case .array(let array):
self = .array(array)
case .pointer(let unsafeMutableRawPointer):
self = .pointer(unsafeMutableRawPointer)
}
}
}
/// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
@@ -44,7 +61,7 @@ public struct ByteBuffer {
private let capacity: Int
/// Retained blob of data that requires the storage to retain a pointer to.
@usableFromInline
var retainedBlob: Blob
let retainedBlob: Blob
@usableFromInline
init(count: Int) {
@@ -57,9 +74,9 @@ public struct ByteBuffer {
}
@usableFromInline
init(blob: Blob, capacity count: Int) {
init(blob: borrowing Blob, capacity count: Int) {
capacity = count
retainedBlob = blob
retainedBlob = .init(blob)
isOwned = false
}
@@ -276,7 +293,7 @@ public struct ByteBuffer {
/// - removeBytes: Removes a number of bytes from the current size
@inline(__always)
init(
blob: Storage.Blob,
blob: borrowing Storage.Blob,
count: Int,
removing removeBytes: Int)
{
@@ -316,7 +333,7 @@ public struct ByteBuffer {
/// - def: Type of the object
/// - position: the index of the object in the buffer
@inline(__always)
public func read<T>(def: T.Type, position: Int) -> T {
public func read<T: BitwiseCopyable>(def: T.Type, position: Int) -> T {
_storage.readWithUnsafeRawPointer(position: position) {
$0.bindMemory(to: T.self, capacity: 1)
.pointee
@@ -360,10 +377,10 @@ public struct ByteBuffer {
@inline(__always)
func readSizedScalar<
T: BinaryInteger,
T1: BinaryInteger,
T2: BinaryInteger,
T3: BinaryInteger,
T4: BinaryInteger
T1: BinaryInteger & BitwiseCopyable,
T2: BinaryInteger & BitwiseCopyable,
T3: BinaryInteger & BitwiseCopyable,
T4: BinaryInteger & BitwiseCopyable
>(
def: T.Type,
t1: T1.Type,

View File

@@ -31,7 +31,7 @@ struct _InternalByteBuffer {
@usableFromInline
final class Storage {
/// pointer to the start of the buffer object in memory
var memory: UnsafeMutableRawPointer
@exclusivity(unchecked) private(set) var memory: UnsafeMutableRawPointer
@usableFromInline
init(count: Int, alignment: Int) {