Skip to content

Commit 2d052a0

Browse files
An additional benchmark for KeyPath read performance. (#61795)
* Added a benchmark for KeyPaths where trivially-typed memory is preceded by non-trivially-typed memory. * Reduces the workload of run_KeyPathClassStructs by a factor of 4. The reported time was 1847 us.
1 parent eae0bf2 commit 2d052a0

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

benchmark/single-source/KeyPathPerformanceTests.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ public let benchmarks = [
6666
tags: [.validation, .api],
6767
setUpFunction: setupKeyPathNestedStructs
6868
),
69+
BenchmarkInfo(
70+
name: "KeyPathClassStructs",
71+
runFunction: run_KeyPathClassStructs,
72+
tags: [.validation, .api],
73+
setUpFunction: setupKeyPathNestedStructs
74+
),
6975
]
7076

7177
/**
@@ -97,6 +103,7 @@ class FixedSizeArrayHolder {
97103
var fixedSizeArray100: FixedSizeArray100<ElementType>
98104
var fixedSizeArray10: FixedSizeArray10<ElementType>
99105
var mainArrayForNestedStructs: [A]
106+
var mainArrayForClassStructs: [D1]
100107
var arrayForMutatingGetset: [MutatingGetsetNested1]
101108
var arrayForGet: [GetNested1]
102109
var arrayForOptionals: [Optional1]
@@ -107,6 +114,7 @@ class FixedSizeArrayHolder {
107114
var keypathForOptional: KeyPath<Optional1, Int?>
108115
var keypathForNestedClasses: KeyPath<C1, Int>
109116
var keypathForNonMutatingGetset: WritableKeyPath<M, Int>
117+
var keypathForClassStructs: WritableKeyPath<D1, Int>
110118

111119
// Same order as in KeyPathWritePerformance
112120
var kp46: WritableKeyPath<FixedSizeArray100<ElementType>, ElementType>
@@ -176,6 +184,7 @@ class FixedSizeArrayHolder {
176184
fixedSizeArray100 = initializeFixedSizeArray100()
177185
fixedSizeArray10 = initializeFixedSizeArray10()
178186
mainArrayForNestedStructs = [A]()
187+
mainArrayForClassStructs = [D1]()
179188
arrayForMutatingGetset = [MutatingGetsetNested1]()
180189
arrayForGet = [GetNested1]()
181190
arrayForOptionals = [Optional1]()
@@ -252,6 +261,7 @@ class FixedSizeArrayHolder {
252261
._nestedItemStorage?!._nestedItemStorage?!._storage)
253262
keypathForNestedClasses = identity(\C1.r.r.r.r.a)
254263
keypathForNonMutatingGetset = identity(\M.n.o.p.q.q)
264+
keypathForClassStructs = identity(\D1.b.c.d.e.e)
255265
}
256266
}
257267

@@ -262,6 +272,10 @@ public func setupKeyPathNestedStructs() {
262272
let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedItems)))))
263273
holder.mainArrayForNestedStructs.append(instance)
264274

275+
let classStructInstance = D1(b: D2(b: 0, c: D3(c: 0, d: D4(d: 0,
276+
e: D5(e: expectedIntForNestedItems)))))
277+
holder.mainArrayForClassStructs.append(classStructInstance)
278+
265279
var mutatingGetsetInstance = MutatingGetsetNested1()
266280
mutatingGetsetInstance.nestedItem.nestedItem.nestedItem.nestedItem
267281
.storage = expectedIntForNestedItems
@@ -333,6 +347,36 @@ struct E {
333347
var e: Int
334348
}
335349

350+
// Used for run_KeyPathClassStruct().
351+
class D1 {
352+
var a: Int
353+
var b: D2
354+
init(b: D2)
355+
{
356+
a = 0
357+
self.b = b
358+
}
359+
}
360+
361+
struct D2 {
362+
var b: Int
363+
var c: D3
364+
}
365+
366+
struct D3 {
367+
var c: Int
368+
var d: D4
369+
}
370+
371+
struct D4 {
372+
var d: Int
373+
var e: D5
374+
}
375+
376+
struct D5 {
377+
var e: Int
378+
}
379+
336380
// Used for run_KeyPathNestedClasses()
337381
class C1 {
338382
let a: Int = 0
@@ -1326,6 +1370,36 @@ public func run_KeyPathNestedStructs(n: Int) {
13261370
check(sum == iterationMultipier * n * expectedIntForNestedItems)
13271371
}
13281372

1373+
// This measures the performance of keypath reads where a block of
1374+
// trivially-typed memory is preceded by something else (optionals, reference
1375+
// types, etc.)
1376+
@inline(never)
1377+
public func run_KeyPathClassStructs(n: Int) {
1378+
var sum = 0
1379+
var index = 0
1380+
let iterationMultipier = 500
1381+
1382+
let singleHopKeyPath0: WritableKeyPath<D1, D2> = \D1.b
1383+
let singleHopKeyPath1: WritableKeyPath<D2, D3> = \D2.c
1384+
let singleHopKeyPath2: WritableKeyPath<D3, D4> = \D3.d
1385+
let singleHopKeyPath3: WritableKeyPath<D4, D5> = \D4.e
1386+
let singleHopKeyPath4: WritableKeyPath<D5, Int> = \D5.e
1387+
1388+
let appendedKeyPath = identity(singleHopKeyPath0.appending(path: singleHopKeyPath1)
1389+
.appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3)
1390+
.appending(path: singleHopKeyPath4))
1391+
1392+
let elementCount = FixedSizeArrayHolder.shared.mainArrayForClassStructs.count
1393+
for _ in 1 ... iterationMultipier * n {
1394+
let element = FixedSizeArrayHolder.shared.mainArrayForClassStructs[index]
1395+
sum += element[keyPath: appendedKeyPath]
1396+
index = (index + 1) % elementCount
1397+
}
1398+
check(sum == iterationMultipier * n * expectedIntForNestedItems)
1399+
}
1400+
1401+
1402+
13291403
// This is meant as a baseline, from a timing perspective,
13301404
// for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance().
13311405
// It's currently set to ".skip", but is useful for comparing the performance between keypath operations and direct dot-notation.

0 commit comments

Comments
 (0)