mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-25 16:08:39 +00:00
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:
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user