mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-29 17:22:03 +00:00
[Swift] Migrating benchmarks to a newer lib. (#8168)
* Adds Nativestructs pointer push into ByteBuffer Updates benchmarks & cleanup Adds native struct vector tests * Address PR comments * Add more benchmarks * Some benchmark cleanup * Return back to 1M structs * Tweak Structs benchmark * Moves swift Benchmarks folder from /tests to /benchmarks --------- Co-authored-by: Joakim Hassila <jocke@ordo.one>
This commit is contained in:
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* 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 str10 = (0...9).map { _ -> String in "x" }.joined()
|
||||||
|
let str100 = (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),
|
||||||
|
]
|
||||||
|
|
||||||
|
let metrics: [BenchmarkMetric] = [
|
||||||
|
.cpuTotal,
|
||||||
|
.wallClock,
|
||||||
|
.mallocCountTotal,
|
||||||
|
.releaseCount,
|
||||||
|
.peakMemoryResident,
|
||||||
|
]
|
||||||
|
let maxIterations = 1_000_000
|
||||||
|
let maxDuration: Duration = .seconds(3)
|
||||||
|
let singleConfiguration: Benchmark.Configuration = .init(
|
||||||
|
metrics: metrics,
|
||||||
|
warmupIterations: 1,
|
||||||
|
scalingFactor: .one,
|
||||||
|
maxDuration: maxDuration,
|
||||||
|
maxIterations: maxIterations)
|
||||||
|
let kiloConfiguration: Benchmark.Configuration = .init(
|
||||||
|
metrics: metrics,
|
||||||
|
warmupIterations: 1,
|
||||||
|
scalingFactor: .kilo,
|
||||||
|
maxDuration: maxDuration,
|
||||||
|
maxIterations: maxIterations)
|
||||||
|
let megaConfiguration: Benchmark.Configuration = .init(
|
||||||
|
metrics: metrics,
|
||||||
|
warmupIterations: 1,
|
||||||
|
scalingFactor: .mega,
|
||||||
|
maxDuration: maxDuration,
|
||||||
|
maxIterations: maxIterations)
|
||||||
|
|
||||||
|
Benchmark.defaultConfiguration = megaConfiguration
|
||||||
|
|
||||||
|
Benchmark("Allocating 1GB", configuration: singleConfiguration) { benchmark in
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(FlatBufferBuilder(initialSize: 1_024_000_000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Clearing 1GB", configuration: singleConfiguration) { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1_024_000_000)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.clear())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Strings 10") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.create(string: str10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Strings 100") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.create(string: str100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Vector 1 Bytes") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.createVector(bytes: bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Vector 1 Ints") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.createVector(ints))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Vector 100 Ints") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for i in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.createVector(ints))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Vector 100 Bytes") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for i in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.createVector(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Vector 100 ContiguousBytes") { benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for i in benchmark.scaledIterations {
|
||||||
|
blackHole(fb.createVector(bytes: bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark(
|
||||||
|
"FlatBufferBuilder Add",
|
||||||
|
configuration: kiloConfiguration)
|
||||||
|
{ benchmark in
|
||||||
|
var fb = FlatBufferBuilder(initialSize: 1024 * 1024 * 32)
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
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)
|
||||||
|
blackHole(fb.endTable(at: s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark("Structs") { benchmark in
|
||||||
|
let rawSize = ((16 * 5) * benchmark.scaledIterations.count) / 1024
|
||||||
|
var fb = FlatBufferBuilder(initialSize: Int32(rawSize * 1600))
|
||||||
|
var offsets: [Offset] = []
|
||||||
|
|
||||||
|
benchmark.startMeasurement()
|
||||||
|
for _ in benchmark.scaledIterations {
|
||||||
|
let vector = fb.createVector(
|
||||||
|
ofStructs: array)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// swift-tools-version:5.1
|
// swift-tools-version:5.8
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 Google Inc. All rights reserved.
|
* Copyright 2020 Google Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
@@ -20,15 +20,23 @@ import PackageDescription
|
|||||||
let package = Package(
|
let package = Package(
|
||||||
name: "benchmarks",
|
name: "benchmarks",
|
||||||
platforms: [
|
platforms: [
|
||||||
.macOS(.v10_14),
|
.macOS(.v13),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(path: "../../.."),
|
.package(path: "../.."),
|
||||||
.package(url: "https://github.com/google/swift-benchmark", from: "0.1.0"),
|
.package(
|
||||||
|
url: "https://github.com/ordo-one/package-benchmark",
|
||||||
|
from: "1.12.0"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.executableTarget(
|
||||||
name: "benchmarks",
|
name: "FlatbuffersBenchmarks",
|
||||||
dependencies: ["FlatBuffers",
|
dependencies: [
|
||||||
.product(name: "Benchmark", package: "swift-benchmark")]),
|
.product(name: "FlatBuffers", package: "flatbuffers"),
|
||||||
|
.product(name: "Benchmark", package: "package-benchmark"),
|
||||||
|
],
|
||||||
|
path: "Benchmarks/FlatbuffersBenchmarks",
|
||||||
|
plugins: [
|
||||||
|
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
9
benchmarks/swift/README.md
Normal file
9
benchmarks/swift/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Benchmarks
|
||||||
|
|
||||||
|
To open the benchmarks in xcode use:
|
||||||
|
|
||||||
|
`open --env BENCHMARK_DISABLE_JEMALLOC=true Package.swift`
|
||||||
|
|
||||||
|
or running them directly within terminal using:
|
||||||
|
|
||||||
|
`swift package benchmark`
|
||||||
@@ -261,6 +261,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<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)
|
||||||
|
self._writerSize = self._writerSize &+ ptr.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a `ContiguousBytes` to buffer memory
|
/// Adds a `ContiguousBytes` to buffer memory
|
||||||
/// - Parameter value: bytes to copy
|
/// - Parameter value: bytes to copy
|
||||||
#if swift(>=5.0) && !os(WASI)
|
#if swift(>=5.0) && !os(WASI)
|
||||||
@@ -271,8 +285,8 @@ 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!),
|
UnsafeRawPointer(ptr.baseAddress!),
|
||||||
ptr.count)
|
ptr.count)
|
||||||
self._writerSize = self._writerSize &+ ptr.count
|
self._writerSize = self._writerSize &+ ptr.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -623,9 +623,7 @@ public struct FlatBufferBuilder {
|
|||||||
startVector(
|
startVector(
|
||||||
structs.count * MemoryLayout<T>.size,
|
structs.count * MemoryLayout<T>.size,
|
||||||
elementSize: MemoryLayout<T>.alignment)
|
elementSize: MemoryLayout<T>.alignment)
|
||||||
for i in structs.reversed() {
|
_bb.push(elements: structs)
|
||||||
_ = create(struct: i)
|
|
||||||
}
|
|
||||||
return endVector(len: structs.count)
|
return endVector(len: structs.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ public enum Vector<U, S>: Verifiable where U: Verifiable, S: Verifiable {
|
|||||||
let range = try verifyRange(&verifier, at: position, of: UOffset.self)
|
let range = try verifyRange(&verifier, at: position, of: UOffset.self)
|
||||||
for index in stride(
|
for index in stride(
|
||||||
from: range.start,
|
from: range.start,
|
||||||
to: Int(clamping: range.start &+ (range.count &* MemoryLayout<Int32>.size)),
|
to: Int(
|
||||||
|
clamping: range
|
||||||
|
.start &+ (range.count &* MemoryLayout<Int32>.size)),
|
||||||
by: MemoryLayout<UOffset>.size)
|
by: MemoryLayout<UOffset>.size)
|
||||||
{
|
{
|
||||||
try U.verify(&verifier, at: index, of: U.self)
|
try U.verify(&verifier, at: index, of: U.self)
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
benchmark("10Strings") {
|
|
||||||
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
|
||||||
for _ in 0..<1_000_000 {
|
|
||||||
_ = fb.create(string: "foobarbaz")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark("100Strings") {
|
|
||||||
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
|
||||||
for _ in 0..<1_000_000 {
|
|
||||||
_ = fb.create(string: str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark("100Bytes") {
|
|
||||||
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
|
||||||
for _ in 0..<1_000_000 {
|
|
||||||
_ = fb.createVector(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark("100Bytes-ContiguousBytes") {
|
|
||||||
var fb = FlatBufferBuilder(initialSize: 1<<20)
|
|
||||||
for _ in 0..<1_000_000 {
|
|
||||||
_ = fb.createVector(bytes: bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark("FlatBufferBuilder.add") {
|
|
||||||
var fb = FlatBufferBuilder(initialSize: 1024 * 1024 * 32)
|
|
||||||
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("structs") {
|
|
||||||
let structCount = 1_000_000
|
|
||||||
|
|
||||||
let rawSize = ((16 * 5) * structCount) / 1024
|
|
||||||
|
|
||||||
var fb = FlatBufferBuilder(initialSize: Int32(rawSize * 1600))
|
|
||||||
|
|
||||||
var offsets: [Offset] = []
|
|
||||||
for _ in 0..<structCount {
|
|
||||||
fb.startVector(
|
|
||||||
5 * MemoryLayout<AA>.size,
|
|
||||||
elementSize: MemoryLayout<AA>.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()
|
|
||||||
@@ -67,9 +67,9 @@ final class FlatBuffersNanInfTests: XCTestCase {
|
|||||||
let data = try encoder.encode(reader)
|
let data = try encoder.encode(reader)
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
decoder.nonConformingFloatDecodingStrategy = .convertFromString(
|
decoder.nonConformingFloatDecodingStrategy = .convertFromString(
|
||||||
positiveInfinity: "inf",
|
positiveInfinity: "inf",
|
||||||
negativeInfinity: "-inf",
|
negativeInfinity: "-inf",
|
||||||
nan: "nan")
|
nan: "nan")
|
||||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||||
let value = try decoder.decode(Test.self, from: data)
|
let value = try decoder.decode(Test.self, from: data)
|
||||||
XCTAssertEqual(value.value, 100)
|
XCTAssertEqual(value.value, 100)
|
||||||
|
|||||||
@@ -53,6 +53,26 @@ final class FlatBuffersVectors: XCTestCase {
|
|||||||
// swiftformat:enable all
|
// 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() {
|
func testCreateEmptyIntArray() {
|
||||||
let numbers: [Int32] = []
|
let numbers: [Int32] = []
|
||||||
var b = FlatBufferBuilder(initialSize: 20)
|
var b = FlatBufferBuilder(initialSize: 20)
|
||||||
|
|||||||
Reference in New Issue
Block a user