From 0089a50e72e823beed35502181ef8d0f8b832baa Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Wed, 3 Aug 2022 13:38:57 -0600 Subject: [PATCH 01/18] Add benchmarks that measure KeyPath read and write performance. --- benchmark/CMakeLists.txt | 1 + .../KeyPathPerformanceTests.swift | 964 ++++++++++++++++++ benchmark/utils/main.swift | 2 + 3 files changed, 967 insertions(+) create mode 100644 benchmark/single-source/KeyPathPerformanceTests.swift diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index cc4ad3ec083c6..37e0aa50b6ffb 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -106,6 +106,7 @@ set(SWIFT_BENCH_MODULES single-source/Integrate single-source/IterateData single-source/Join + single-source/KeyPathPerformanceTests single-source/LazyFilter single-source/LinkedList single-source/LuhnAlgoEager diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift new file mode 100644 index 0000000000000..e304ff9fac823 --- /dev/null +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -0,0 +1,964 @@ +//===--- KeyPathPerformanceTests.swift -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo( + name: "KeyPathPerformanceOfNestedStructs", + runFunction: run_KeyPathPerformanceOfNestedStructs, + tags: [.validation, .api] + ), + BenchmarkInfo( + name: "KeyPathDirectAccess", + runFunction: run_testDirectAccess, + tags: [.validation, .api] + ), + BenchmarkInfo( + name: "KeyPathReadPerformance", + runFunction: run_testKeyPathReadPerformance, + tags: [.validation, .api] + ), + BenchmarkInfo( + name: "KeyPathWritePerformance", + runFunction: run_testKeyPathWritePerformance, + tags: [.validation, .api] + ), +] + +// Used for run_KeyPathPerformanceOfNestedStructs() +struct A { + var a: Int + var b: B +} + +struct B { + var b: Int + var c: C +} + +struct C { + var c: Int + var d: D +} + +struct D { + var d: Int + var e: E +} + +struct E { + var e: Int +} + +let singleHopKeyPath0: WritableKeyPath = \A.b +let singleHopKeyPath1: WritableKeyPath = \B.c +let singleHopKeyPath2: WritableKeyPath = \C.d +let singleHopKeyPath3: WritableKeyPath = \D.e +let singleHopKeyPath4: WritableKeyPath = \E.e + +// Used for run_KeyPathFixedSizeArrayAccess() and run_testDirectAccess(). +struct FixedSizeArray100: Sequence, IteratorProtocol { + let count: Int = 100 + + public init( + element0: Element, + element1: Element, + element2: Element, + element3: Element, + element4: Element, + element5: Element, + element6: Element, + element7: Element, + element8: Element, + element9: Element, + element10: Element, + element11: Element, + element12: Element, + element13: Element, + element14: Element, + element15: Element, + element16: Element, + element17: Element, + element18: Element, + element19: Element, + element20: Element, + element21: Element, + element22: Element, + element23: Element, + element24: Element, + element25: Element, + element26: Element, + element27: Element, + element28: Element, + element29: Element, + element30: Element, + element31: Element, + element32: Element, + element33: Element, + element34: Element, + element35: Element, + element36: Element, + element37: Element, + element38: Element, + element39: Element, + element40: Element, + element41: Element, + element42: Element, + element43: Element, + element44: Element, + element45: Element, + element46: Element, + element47: Element, + element48: Element, + element49: Element, + element50: Element, + element51: Element, + element52: Element, + element53: Element, + element54: Element, + element55: Element, + element56: Element, + element57: Element, + element58: Element, + element59: Element, + element60: Element, + element61: Element, + element62: Element, + element63: Element, + element64: Element, + element65: Element, + element66: Element, + element67: Element, + element68: Element, + element69: Element, + element70: Element, + element71: Element, + element72: Element, + element73: Element, + element74: Element, + element75: Element, + element76: Element, + element77: Element, + element78: Element, + element79: Element, + element80: Element, + element81: Element, + element82: Element, + element83: Element, + element84: Element, + element85: Element, + element86: Element, + element87: Element, + element88: Element, + element89: Element, + element90: Element, + element91: Element, + element92: Element, + element93: Element, + element94: Element, + element95: Element, + element96: Element, + element97: Element, + element98: Element, + element99: Element + ) { + self.element0 = element0 + self.element1 = element1 + self.element2 = element2 + self.element3 = element3 + self.element4 = element4 + self.element5 = element5 + self.element6 = element6 + self.element7 = element7 + self.element8 = element8 + self.element9 = element9 + self.element10 = element10 + self.element11 = element11 + self.element12 = element12 + self.element13 = element13 + self.element14 = element14 + self.element15 = element15 + self.element16 = element16 + self.element17 = element17 + self.element18 = element18 + self.element19 = element19 + self.element20 = element20 + self.element21 = element21 + self.element22 = element22 + self.element23 = element23 + self.element24 = element24 + self.element25 = element25 + self.element26 = element26 + self.element27 = element27 + self.element28 = element28 + self.element29 = element29 + self.element30 = element30 + self.element31 = element31 + self.element32 = element32 + self.element33 = element33 + self.element34 = element34 + self.element35 = element35 + self.element36 = element36 + self.element37 = element37 + self.element38 = element38 + self.element39 = element39 + self.element40 = element40 + self.element41 = element41 + self.element42 = element42 + self.element43 = element43 + self.element44 = element44 + self.element45 = element45 + self.element46 = element46 + self.element47 = element47 + self.element48 = element48 + self.element49 = element49 + self.element50 = element50 + self.element51 = element51 + self.element52 = element52 + self.element53 = element53 + self.element54 = element54 + self.element55 = element55 + self.element56 = element56 + self.element57 = element57 + self.element58 = element58 + self.element59 = element59 + self.element60 = element60 + self.element61 = element61 + self.element62 = element62 + self.element63 = element63 + self.element64 = element64 + self.element65 = element65 + self.element66 = element66 + self.element67 = element67 + self.element68 = element68 + self.element69 = element69 + self.element70 = element70 + self.element71 = element71 + self.element72 = element72 + self.element73 = element73 + self.element74 = element74 + self.element75 = element75 + self.element76 = element76 + self.element77 = element77 + self.element78 = element78 + self.element79 = element79 + self.element80 = element80 + self.element81 = element81 + self.element82 = element82 + self.element83 = element83 + self.element84 = element84 + self.element85 = element85 + self.element86 = element86 + self.element87 = element87 + self.element88 = element88 + self.element89 = element89 + self.element90 = element90 + self.element91 = element91 + self.element92 = element92 + self.element93 = element93 + self.element94 = element94 + self.element95 = element95 + self.element96 = element96 + self.element97 = element97 + self.element98 = element98 + self.element99 = element99 + } + + var element0: Element + var element1: Element + var element2: Element + var element3: Element + var element4: Element + var element5: Element + var element6: Element + var element7: Element + var element8: Element + var element9: Element + var element10: Element + var element11: Element + var element12: Element + var element13: Element + var element14: Element + var element15: Element + var element16: Element + var element17: Element + var element18: Element + var element19: Element + var element20: Element + var element21: Element + var element22: Element + var element23: Element + var element24: Element + var element25: Element + var element26: Element + var element27: Element + var element28: Element + var element29: Element + var element30: Element + var element31: Element + var element32: Element + var element33: Element + var element34: Element + var element35: Element + var element36: Element + var element37: Element + var element38: Element + var element39: Element + var element40: Element + var element41: Element + var element42: Element + var element43: Element + var element44: Element + var element45: Element + var element46: Element + var element47: Element + var element48: Element + var element49: Element + var element50: Element + var element51: Element + var element52: Element + var element53: Element + var element54: Element + var element55: Element + var element56: Element + var element57: Element + var element58: Element + var element59: Element + var element60: Element + var element61: Element + var element62: Element + var element63: Element + var element64: Element + var element65: Element + var element66: Element + var element67: Element + var element68: Element + var element69: Element + var element70: Element + var element71: Element + var element72: Element + var element73: Element + var element74: Element + var element75: Element + var element76: Element + var element77: Element + var element78: Element + var element79: Element + var element80: Element + var element81: Element + var element82: Element + var element83: Element + var element84: Element + var element85: Element + var element86: Element + var element87: Element + var element88: Element + var element89: Element + var element90: Element + var element91: Element + var element92: Element + var element93: Element + var element94: Element + var element95: Element + var element96: Element + var element97: Element + var element98: Element + var element99: Element + + static func getKeypathToElement(index: Int) -> WritableKeyPath, Element> { + switch index { + case 0: + return \FixedSizeArray100.element0 + case 1: + return \FixedSizeArray100.element1 + case 2: + return \FixedSizeArray100.element2 + case 3: + return \FixedSizeArray100.element3 + case 4: + return \FixedSizeArray100.element4 + case 5: + return \FixedSizeArray100.element5 + case 6: + return \FixedSizeArray100.element6 + case 7: + return \FixedSizeArray100.element7 + case 8: + return \FixedSizeArray100.element8 + case 9: + return \FixedSizeArray100.element9 + case 10: + return \FixedSizeArray100.element10 + case 11: + return \FixedSizeArray100.element11 + case 12: + return \FixedSizeArray100.element12 + case 13: + return \FixedSizeArray100.element13 + case 14: + return \FixedSizeArray100.element14 + case 15: + return \FixedSizeArray100.element15 + case 16: + return \FixedSizeArray100.element16 + case 17: + return \FixedSizeArray100.element17 + case 18: + return \FixedSizeArray100.element18 + case 19: + return \FixedSizeArray100.element19 + case 20: + return \FixedSizeArray100.element20 + case 21: + return \FixedSizeArray100.element21 + case 22: + return \FixedSizeArray100.element22 + case 23: + return \FixedSizeArray100.element23 + case 24: + return \FixedSizeArray100.element24 + case 25: + return \FixedSizeArray100.element25 + case 26: + return \FixedSizeArray100.element26 + case 27: + return \FixedSizeArray100.element27 + case 28: + return \FixedSizeArray100.element28 + case 29: + return \FixedSizeArray100.element29 + case 30: + return \FixedSizeArray100.element30 + case 31: + return \FixedSizeArray100.element31 + case 32: + return \FixedSizeArray100.element32 + case 33: + return \FixedSizeArray100.element33 + case 34: + return \FixedSizeArray100.element34 + case 35: + return \FixedSizeArray100.element35 + case 36: + return \FixedSizeArray100.element36 + case 37: + return \FixedSizeArray100.element37 + case 38: + return \FixedSizeArray100.element38 + case 39: + return \FixedSizeArray100.element39 + case 40: + return \FixedSizeArray100.element40 + case 41: + return \FixedSizeArray100.element41 + case 42: + return \FixedSizeArray100.element42 + case 43: + return \FixedSizeArray100.element43 + case 44: + return \FixedSizeArray100.element44 + case 45: + return \FixedSizeArray100.element45 + case 46: + return \FixedSizeArray100.element46 + case 47: + return \FixedSizeArray100.element47 + case 48: + return \FixedSizeArray100.element48 + case 49: + return \FixedSizeArray100.element49 + case 50: + return \FixedSizeArray100.element50 + case 51: + return \FixedSizeArray100.element51 + case 52: + return \FixedSizeArray100.element52 + case 53: + return \FixedSizeArray100.element53 + case 54: + return \FixedSizeArray100.element54 + case 55: + return \FixedSizeArray100.element55 + case 56: + return \FixedSizeArray100.element56 + case 57: + return \FixedSizeArray100.element57 + case 58: + return \FixedSizeArray100.element58 + case 59: + return \FixedSizeArray100.element59 + case 60: + return \FixedSizeArray100.element60 + case 61: + return \FixedSizeArray100.element61 + case 62: + return \FixedSizeArray100.element62 + case 63: + return \FixedSizeArray100.element63 + case 64: + return \FixedSizeArray100.element64 + case 65: + return \FixedSizeArray100.element65 + case 66: + return \FixedSizeArray100.element66 + case 67: + return \FixedSizeArray100.element67 + case 68: + return \FixedSizeArray100.element68 + case 69: + return \FixedSizeArray100.element69 + case 70: + return \FixedSizeArray100.element70 + case 71: + return \FixedSizeArray100.element71 + case 72: + return \FixedSizeArray100.element72 + case 73: + return \FixedSizeArray100.element73 + case 74: + return \FixedSizeArray100.element74 + case 75: + return \FixedSizeArray100.element75 + case 76: + return \FixedSizeArray100.element76 + case 77: + return \FixedSizeArray100.element77 + case 78: + return \FixedSizeArray100.element78 + case 79: + return \FixedSizeArray100.element79 + case 80: + return \FixedSizeArray100.element80 + case 81: + return \FixedSizeArray100.element81 + case 82: + return \FixedSizeArray100.element82 + case 83: + return \FixedSizeArray100.element83 + case 84: + return \FixedSizeArray100.element84 + case 85: + return \FixedSizeArray100.element85 + case 86: + return \FixedSizeArray100.element86 + case 87: + return \FixedSizeArray100.element87 + case 88: + return \FixedSizeArray100.element88 + case 89: + return \FixedSizeArray100.element89 + case 90: + return \FixedSizeArray100.element90 + case 91: + return \FixedSizeArray100.element91 + case 92: + return \FixedSizeArray100.element92 + case 93: + return \FixedSizeArray100.element93 + case 94: + return \FixedSizeArray100.element94 + case 95: + return \FixedSizeArray100.element95 + case 96: + return \FixedSizeArray100.element96 + case 97: + return \FixedSizeArray100.element97 + case 98: + return \FixedSizeArray100.element98 + case 99: + return \FixedSizeArray100.element99 + default: + fatalError() + } + } + + // Conformance to Iterator. + mutating func next() -> Element? { + var iter_count = 0 + if iter_count == count { + return nil + } else { + defer { iter_count += 1 } + return self[keyPath: FixedSizeArray100.getKeypathToElement(index: iter_count)] + } + } +} + +let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) +let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) +let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) +let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) +let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) +let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) +let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) +let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) +let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) +let kp9 = FixedSizeArray100.getKeypathToElement(index: 9) +let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) +let kp11 = FixedSizeArray100.getKeypathToElement(index: 11) +let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) +let kp13 = FixedSizeArray100.getKeypathToElement(index: 13) +let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) +let kp15 = FixedSizeArray100.getKeypathToElement(index: 15) +let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) +let kp17 = FixedSizeArray100.getKeypathToElement(index: 17) +let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) +let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) +let kp20 = FixedSizeArray100.getKeypathToElement(index: 20) +let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) +let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) +let kp23 = FixedSizeArray100.getKeypathToElement(index: 23) +let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) +let kp25 = FixedSizeArray100.getKeypathToElement(index: 25) +let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) +let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) +let kp28 = FixedSizeArray100.getKeypathToElement(index: 28) +let kp29 = FixedSizeArray100.getKeypathToElement(index: 29) +let kp30 = FixedSizeArray100.getKeypathToElement(index: 30) +let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) +let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) +let kp33 = FixedSizeArray100.getKeypathToElement(index: 33) +let kp34 = FixedSizeArray100.getKeypathToElement(index: 34) +let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) +let kp36 = FixedSizeArray100.getKeypathToElement(index: 36) +let kp37 = FixedSizeArray100.getKeypathToElement(index: 37) +let kp38 = FixedSizeArray100.getKeypathToElement(index: 38) +let kp39 = FixedSizeArray100.getKeypathToElement(index: 39) +let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) +let kp41 = FixedSizeArray100.getKeypathToElement(index: 41) +let kp42 = FixedSizeArray100.getKeypathToElement(index: 42) +let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) +let kp44 = FixedSizeArray100.getKeypathToElement(index: 44) +let kp45 = FixedSizeArray100.getKeypathToElement(index: 45) +let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) +let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) +let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) +let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) +let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) +let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) +let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) +let kp53 = FixedSizeArray100.getKeypathToElement(index: 53) +let kp54 = FixedSizeArray100.getKeypathToElement(index: 54) +let kp55 = FixedSizeArray100.getKeypathToElement(index: 55) +let kp56 = FixedSizeArray100.getKeypathToElement(index: 56) +let kp57 = FixedSizeArray100.getKeypathToElement(index: 57) +let kp58 = FixedSizeArray100.getKeypathToElement(index: 58) +let kp59 = FixedSizeArray100.getKeypathToElement(index: 59) +let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) +let kp61 = FixedSizeArray100.getKeypathToElement(index: 61) +let kp62 = FixedSizeArray100.getKeypathToElement(index: 62) +let kp63 = FixedSizeArray100.getKeypathToElement(index: 63) +let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) +let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) +let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) +let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) +let kp68 = FixedSizeArray100.getKeypathToElement(index: 68) +let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) +let kp70 = FixedSizeArray100.getKeypathToElement(index: 70) +let kp71 = FixedSizeArray100.getKeypathToElement(index: 71) +let kp72 = FixedSizeArray100.getKeypathToElement(index: 72) +let kp73 = FixedSizeArray100.getKeypathToElement(index: 73) +let kp74 = FixedSizeArray100.getKeypathToElement(index: 74) +let kp75 = FixedSizeArray100.getKeypathToElement(index: 75) +let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) +let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) +let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) +let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) +let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) +let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) +let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) +let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) +let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) +let kp85 = FixedSizeArray100.getKeypathToElement(index: 85) +let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) +let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) +let kp88 = FixedSizeArray100.getKeypathToElement(index: 88) +let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) +let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) +let kp91 = FixedSizeArray100.getKeypathToElement(index: 91) +let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) +let kp93 = FixedSizeArray100.getKeypathToElement(index: 93) +let kp94 = FixedSizeArray100.getKeypathToElement(index: 94) +let kp95 = FixedSizeArray100.getKeypathToElement(index: 95) +let kp96 = FixedSizeArray100.getKeypathToElement(index: 96) +let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) +let kp98 = FixedSizeArray100.getKeypathToElement(index: 98) +let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) + +func initializeFixedSizeArray100() -> FixedSizeArray100 { + return FixedSizeArray100( + element0: 0, + element1: 1, + element2: 2, + element3: 3, + element4: 4, + element5: 5, + element6: 6, + element7: 7, + element8: 8, + element9: 9, + element10: 10, + element11: 11, + element12: 12, + element13: 13, + element14: 14, + element15: 15, + element16: 16, + element17: 17, + element18: 18, + element19: 19, + element20: 20, + element21: 21, + element22: 22, + element23: 23, + element24: 24, + element25: 25, + element26: 26, + element27: 27, + element28: 28, + element29: 29, + element30: 30, + element31: 31, + element32: 32, + element33: 33, + element34: 34, + element35: 35, + element36: 36, + element37: 37, + element38: 38, + element39: 39, + element40: 40, + element41: 41, + element42: 42, + element43: 43, + element44: 44, + element45: 45, + element46: 46, + element47: 47, + element48: 48, + element49: 49, + element50: 50, + element51: 51, + element52: 52, + element53: 53, + element54: 54, + element55: 55, + element56: 56, + element57: 57, + element58: 58, + element59: 59, + element60: 60, + element61: 61, + element62: 62, + element63: 63, + element64: 64, + element65: 65, + element66: 66, + element67: 67, + element68: 68, + element69: 69, + element70: 70, + element71: 71, + element72: 72, + element73: 73, + element74: 74, + element75: 75, + element76: 76, + element77: 77, + element78: 78, + element79: 79, + element80: 80, + element81: 81, + element82: 82, + element83: 83, + element84: 84, + element85: 85, + element86: 86, + element87: 87, + element88: 88, + element89: 89, + element90: 90, + element91: 91, + element92: 92, + element93: 93, + element94: 94, + element95: 95, + element96: 96, + element97: 97, + element98: 98, + element99: 99 + ) +} + +func setupKeyPathArray() -> [AnyKeyPath] { + var arrayOfKeyPaths = [kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9] + arrayOfKeyPaths.append(contentsOf: [kp10, kp11, kp12, kp13, kp14, kp15, kp16, kp17, kp18, kp19]) + arrayOfKeyPaths.append(contentsOf: [kp20, kp21, kp22, kp23, kp24, kp25, kp26, kp27, kp28, kp29]) + arrayOfKeyPaths.append(contentsOf: [kp30, kp31, kp32, kp33, kp34, kp35, kp36, kp37, kp38, kp39]) + arrayOfKeyPaths.append(contentsOf: [kp40, kp41, kp42, kp43, kp44, kp45, kp46, kp47, kp48, kp49]) + arrayOfKeyPaths.append(contentsOf: [kp50, kp51, kp52, kp53, kp54, kp55, kp56, kp57, kp58, kp59]) + arrayOfKeyPaths.append(contentsOf: [kp60, kp61, kp62, kp63, kp64, kp65, kp66, kp67, kp68, kp69]) + arrayOfKeyPaths.append(contentsOf: [kp70, kp71, kp72, kp73, kp74, kp75, kp76, kp77, kp78, kp79]) + arrayOfKeyPaths.append(contentsOf: [kp80, kp81, kp82, kp83, kp84, kp85, kp86, kp87, kp88, kp89]) + arrayOfKeyPaths.append(contentsOf: [kp90, kp91, kp92, kp93, kp94, kp95, kp96, kp97, kp98, kp99]) + + return arrayOfKeyPaths +} + +@inline(never) +public func run_KeyPathPerformanceOfNestedStructs(n _: Int) { + let numberOfInstances = 250_000 + let expectedInt = 3 + var mainArray = [A]() + for _ in 0 ..< numberOfInstances { + let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedInt))))) + mainArray.append(instance) + } + let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1).appending(path: singleHopKeyPath2) + .appending(path: singleHopKeyPath3).appending(path: singleHopKeyPath4) + var sum = 0 + + for el in mainArray { + sum += el[keyPath: appendedKeyPath] + } + assert(sum == numberOfInstances * expectedInt) +} + +let numberOfIterations = 100_000 +let expectedSum = 1.6669792312063853e+24 // Based on 100,000 iterations. + +// This is meant as a baseline, from a timing perspective, +// for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance(). +@inline(never) +public func run_testDirectAccess(n _: Int) { + var sum = Double(0) + var fixedSizeArray100 = initializeFixedSizeArray100() + for t in 0 ..< numberOfIterations { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 + fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100.element27 + fixedSizeArray100.element81 += fixedSizeArray100.element49 / fixedSizeArray100.element9 + fixedSizeArray100.element31 += fixedSizeArray100.element90 + fixedSizeArray100.element84 + fixedSizeArray100.element35 += fixedSizeArray100.element6 - fixedSizeArray100.element22 + fixedSizeArray100.element14 += fixedSizeArray100.element67 * fixedSizeArray100.element82 + fixedSizeArray100.element51 += fixedSizeArray100.element40 / fixedSizeArray100.element86 + fixedSizeArray100.element24 += fixedSizeArray100.element23 + fixedSizeArray100.element49 + fixedSizeArray100.element90 += fixedSizeArray100.element95 - fixedSizeArray100.element18 + fixedSizeArray100.element80 += fixedSizeArray100.element45 * fixedSizeArray100.element97 + fixedSizeArray100.element47 += fixedSizeArray100.element65 / fixedSizeArray100.element69 + fixedSizeArray100.element92 += fixedSizeArray100.element80 + fixedSizeArray100.element76 + fixedSizeArray100.element78 += fixedSizeArray100.element32 - fixedSizeArray100.element32 + fixedSizeArray100.element79 += fixedSizeArray100.element59 * fixedSizeArray100.element52 + fixedSizeArray100.element46 += fixedSizeArray100.element60 / fixedSizeArray100.element24 + fixedSizeArray100.element64 += fixedSizeArray100.element41 + fixedSizeArray100.element87 + fixedSizeArray100.element65 += fixedSizeArray100.element72 - fixedSizeArray100.element67 + fixedSizeArray100.element19 += fixedSizeArray100.element81 * fixedSizeArray100.element65 + fixedSizeArray100.element66 += fixedSizeArray100.element55 / fixedSizeArray100.element43 + fixedSizeArray100.element10 += fixedSizeArray100.element52 + fixedSizeArray100.element12 + fixedSizeArray100.element81 += fixedSizeArray100.element50 - fixedSizeArray100.element21 + fixedSizeArray100.element16 += fixedSizeArray100.element80 * fixedSizeArray100.element77 + fixedSizeArray100.element6 += fixedSizeArray100.element62 / fixedSizeArray100.element40 + fixedSizeArray100.element26 += fixedSizeArray100.element65 + fixedSizeArray100.element65 + fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100.element50 + } + + let keyPathArray = setupKeyPathArray() + for kp in keyPathArray { + sum += fixedSizeArray100[keyPath: kp]! as! Double + } + assert(sum == expectedSum) +} + +// This measures read performance of keypaths. +// It has the same number of keypath reads as run_testKeyPathWritePerformance() has writes. +@inline(never) +public func run_testKeyPathReadPerformance(n _: Int) { + var sum = Double(0) + var fixedSizeArray100 = initializeFixedSizeArray100() + for t in 0 ..< numberOfIterations { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100[keyPath: kp99] + fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100[keyPath: kp27] + fixedSizeArray100.element81 += fixedSizeArray100.element49 / fixedSizeArray100[keyPath: kp9] + fixedSizeArray100.element31 += fixedSizeArray100.element90 + fixedSizeArray100[keyPath: kp84] + fixedSizeArray100.element35 += fixedSizeArray100.element6 - fixedSizeArray100[keyPath: kp22] + fixedSizeArray100.element14 += fixedSizeArray100.element67 * fixedSizeArray100[keyPath: kp82] + fixedSizeArray100.element51 += fixedSizeArray100.element40 / fixedSizeArray100[keyPath: kp86] + fixedSizeArray100.element24 += fixedSizeArray100.element23 + fixedSizeArray100[keyPath: kp49] + fixedSizeArray100.element90 += fixedSizeArray100.element95 - fixedSizeArray100[keyPath: kp18] + fixedSizeArray100.element80 += fixedSizeArray100.element45 * fixedSizeArray100[keyPath: kp97] + fixedSizeArray100.element47 += fixedSizeArray100.element65 / fixedSizeArray100[keyPath: kp69] + fixedSizeArray100.element92 += fixedSizeArray100.element80 + fixedSizeArray100[keyPath: kp76] + fixedSizeArray100.element78 += fixedSizeArray100.element32 - fixedSizeArray100[keyPath: kp32] + fixedSizeArray100.element79 += fixedSizeArray100.element59 * fixedSizeArray100[keyPath: kp52] + fixedSizeArray100.element46 += fixedSizeArray100.element60 / fixedSizeArray100[keyPath: kp24] + fixedSizeArray100.element64 += fixedSizeArray100.element41 + fixedSizeArray100[keyPath: kp87] + fixedSizeArray100.element65 += fixedSizeArray100.element72 - fixedSizeArray100[keyPath: kp67] + fixedSizeArray100.element19 += fixedSizeArray100.element81 * fixedSizeArray100[keyPath: kp65] + fixedSizeArray100.element66 += fixedSizeArray100.element55 / fixedSizeArray100[keyPath: kp43] + fixedSizeArray100.element10 += fixedSizeArray100.element52 + fixedSizeArray100[keyPath: kp12] + fixedSizeArray100.element81 += fixedSizeArray100.element50 - fixedSizeArray100[keyPath: kp21] + fixedSizeArray100.element16 += fixedSizeArray100.element80 * fixedSizeArray100[keyPath: kp77] + fixedSizeArray100.element6 += fixedSizeArray100.element62 / fixedSizeArray100[keyPath: kp40] + fixedSizeArray100.element26 += fixedSizeArray100.element65 + fixedSizeArray100[keyPath: kp65] + fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100[keyPath: kp50] + } + + let keyPathArray = setupKeyPathArray() + for kp in keyPathArray { + sum += fixedSizeArray100[keyPath: kp]! as! Double + } + assert(sum == expectedSum) +} + +// This measures write performance of keypaths. +// It has the same number of keypath writes as run_testKeyPathReadPerformance() has reads. +@inline(never) +public func run_testKeyPathWritePerformance(n _: Int) { + var sum = Double(0) + var fixedSizeArray100 = initializeFixedSizeArray100() + for t in 0 ..< numberOfIterations { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100.element99 + fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100.element27 + fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100.element9 + fixedSizeArray100[keyPath: kp31] += fixedSizeArray100.element90 + fixedSizeArray100.element84 + fixedSizeArray100[keyPath: kp35] += fixedSizeArray100.element6 - fixedSizeArray100.element22 + fixedSizeArray100[keyPath: kp14] += fixedSizeArray100.element67 * fixedSizeArray100.element82 + fixedSizeArray100[keyPath: kp51] += fixedSizeArray100.element40 / fixedSizeArray100.element86 + fixedSizeArray100[keyPath: kp24] += fixedSizeArray100.element23 + fixedSizeArray100.element49 + fixedSizeArray100[keyPath: kp90] += fixedSizeArray100.element95 - fixedSizeArray100.element18 + fixedSizeArray100[keyPath: kp80] += fixedSizeArray100.element45 * fixedSizeArray100.element97 + fixedSizeArray100[keyPath: kp47] += fixedSizeArray100.element65 / fixedSizeArray100.element69 + fixedSizeArray100[keyPath: kp92] += fixedSizeArray100.element80 + fixedSizeArray100.element76 + fixedSizeArray100[keyPath: kp78] += fixedSizeArray100.element32 - fixedSizeArray100.element32 + fixedSizeArray100[keyPath: kp79] += fixedSizeArray100.element59 * fixedSizeArray100.element52 + fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element60 / fixedSizeArray100.element24 + fixedSizeArray100[keyPath: kp64] += fixedSizeArray100.element41 + fixedSizeArray100.element87 + fixedSizeArray100[keyPath: kp65] += fixedSizeArray100.element72 - fixedSizeArray100.element67 + fixedSizeArray100[keyPath: kp19] += fixedSizeArray100.element81 * fixedSizeArray100.element65 + fixedSizeArray100[keyPath: kp66] += fixedSizeArray100.element55 / fixedSizeArray100.element43 + fixedSizeArray100[keyPath: kp10] += fixedSizeArray100.element52 + fixedSizeArray100.element12 + fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element50 - fixedSizeArray100.element21 + fixedSizeArray100[keyPath: kp16] += fixedSizeArray100.element80 * fixedSizeArray100.element77 + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100.element62 / fixedSizeArray100.element40 + fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100.element65 + fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100.element50 + } + + let keyPathArray = setupKeyPathArray() + for kp in keyPathArray { + sum += fixedSizeArray100[keyPath: kp]! as! Double + } + assert(sum == expectedSum) +} diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 9470afc27d258..d330e60eb69cd 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -97,6 +97,7 @@ import IntegerParsing import Integrate import IterateData import Join +import KeyPathPerformanceTests import LazyFilter import LinkedList import LuhnAlgoEager @@ -279,6 +280,7 @@ register(Integrate.benchmarks) register(IterateData.benchmarks) register(Join.benchmarks) register(LazyFilter.benchmarks) +register(KeyPathPerformanceTests.benchmarks) register(LinkedList.benchmarks) register(LuhnAlgoEager.benchmarks) register(LuhnAlgoLazy.benchmarks) From 7c4fd3754f9b774dc9506211e57ab53d6683be0f Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 5 Aug 2022 10:17:41 -0600 Subject: [PATCH 02/18] Added setUpFunctions. Revised number of iterations per benchmark. --- .../KeyPathPerformanceTests.swift | 363 ++++++++++-------- 1 file changed, 202 insertions(+), 161 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index e304ff9fac823..8796567406f31 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -9,33 +9,100 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// - import TestsUtils public let benchmarks = [ BenchmarkInfo( - name: "KeyPathPerformanceOfNestedStructs", - runFunction: run_KeyPathPerformanceOfNestedStructs, - tags: [.validation, .api] + name: "KeyPathNestedStructs", + runFunction: run_KeyPathNestedStructs, + tags: [.validation, .api], + setUpFunction: setupKeyPathNestedStructs ), BenchmarkInfo( name: "KeyPathDirectAccess", runFunction: run_testDirectAccess, - tags: [.validation, .api] + tags: [.validation, .api], + setUpFunction: setupSingleton ), BenchmarkInfo( name: "KeyPathReadPerformance", runFunction: run_testKeyPathReadPerformance, - tags: [.validation, .api] + tags: [.validation, .api], + setUpFunction: setupSingleton ), BenchmarkInfo( name: "KeyPathWritePerformance", runFunction: run_testKeyPathWritePerformance, - tags: [.validation, .api] + tags: [.validation, .api], + setUpFunction: setupSingleton ), ] -// Used for run_KeyPathPerformanceOfNestedStructs() +// Number of iterations for each benchmark. +let numberOfIterationsForNestedStructs = 20000 +let numberOfIterationsForDirectAccess = 1_000_000 +let numberOfIterationsForKeyPathReadPerformance = 2000 +let numberOfIterationsForKeyPathWritePerformance = 12000 + +// Make it easy to switch between primitives of different types (Int, Float, Double, etc.). +typealias ElementType = Double + +// Variables used across the benchmarks +let expectedIntForNestedStructs = 3 +let arrayHolder = FixedSizeArrayHolder() + +let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1) + .appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3) + .appending(path: singleHopKeyPath4) + +// The purpose of this class is to hold arrays used during the course of the benchmarks. +// This is to avoid storing them in the global scope, which can slow things down. +// We force instantiation in the setUpFunction so that we exclude the setup time from the benchmark time itself, +// otherwise the instance would be lazily instantiated, and thereby add to the benchmark time. +class FixedSizeArrayHolder { + var fixedSizeArray100: FixedSizeArray100 + var mainArrayForNestedStructs: [A] + static let shared = FixedSizeArrayHolder() + init() { + fixedSizeArray100 = initializeFixedSizeArray100() + mainArrayForNestedStructs = [A]() + } + + func reset() { + fixedSizeArray100 = initializeFixedSizeArray100() + } + + func forceInstantiation() {} +} + +// Setup Functions. +public func setupKeyPathNestedStructs() { + FixedSizeArrayHolder.shared.forceInstantiation() + for _ in 0 ..< numberOfIterationsForNestedStructs { + let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedStructs))))) + FixedSizeArrayHolder.shared.mainArrayForNestedStructs.append(instance) + } +} + +public func setupSingleton() { + FixedSizeArrayHolder.shared.forceInstantiation() +} + +func computeSumOfFixedSizeArray100(fixedSizeArray100: inout FixedSizeArray100) + -> ElementType +{ + var sum = ElementType(0) + withUnsafeBytes(of: fixedSizeArray100) { + var pointer = $0.baseAddress.unsafelyUnwrapped + for _ in 0 ..< fixedSizeArray100.count { + sum += pointer.assumingMemoryBound(to: ElementType.self).pointee + pointer = pointer.advanced(by: MemoryLayout.size) + } + } + return sum +} + +// Used for run_KeyPathNestedStructs(). struct A { var a: Int var b: B @@ -375,7 +442,9 @@ struct FixedSizeArray100: Sequence, IteratorProtocol { var element98: Element var element99: Element - static func getKeypathToElement(index: Int) -> WritableKeyPath, Element> { + static func getKeypathToElement(index: Int) + -> WritableKeyPath, Element> + { switch index { case 0: return \FixedSizeArray100.element0 @@ -594,109 +663,109 @@ struct FixedSizeArray100: Sequence, IteratorProtocol { } } -let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) -let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) -let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) -let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) -let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) -let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) -let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) -let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) -let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) -let kp9 = FixedSizeArray100.getKeypathToElement(index: 9) -let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) -let kp11 = FixedSizeArray100.getKeypathToElement(index: 11) -let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) -let kp13 = FixedSizeArray100.getKeypathToElement(index: 13) -let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) -let kp15 = FixedSizeArray100.getKeypathToElement(index: 15) -let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) -let kp17 = FixedSizeArray100.getKeypathToElement(index: 17) -let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) -let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) -let kp20 = FixedSizeArray100.getKeypathToElement(index: 20) -let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) -let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) -let kp23 = FixedSizeArray100.getKeypathToElement(index: 23) -let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) -let kp25 = FixedSizeArray100.getKeypathToElement(index: 25) -let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) -let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) -let kp28 = FixedSizeArray100.getKeypathToElement(index: 28) -let kp29 = FixedSizeArray100.getKeypathToElement(index: 29) -let kp30 = FixedSizeArray100.getKeypathToElement(index: 30) -let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) -let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) -let kp33 = FixedSizeArray100.getKeypathToElement(index: 33) -let kp34 = FixedSizeArray100.getKeypathToElement(index: 34) -let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) -let kp36 = FixedSizeArray100.getKeypathToElement(index: 36) -let kp37 = FixedSizeArray100.getKeypathToElement(index: 37) -let kp38 = FixedSizeArray100.getKeypathToElement(index: 38) -let kp39 = FixedSizeArray100.getKeypathToElement(index: 39) -let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) -let kp41 = FixedSizeArray100.getKeypathToElement(index: 41) -let kp42 = FixedSizeArray100.getKeypathToElement(index: 42) -let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) -let kp44 = FixedSizeArray100.getKeypathToElement(index: 44) -let kp45 = FixedSizeArray100.getKeypathToElement(index: 45) -let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) -let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) -let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) -let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) -let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) -let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) -let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) -let kp53 = FixedSizeArray100.getKeypathToElement(index: 53) -let kp54 = FixedSizeArray100.getKeypathToElement(index: 54) -let kp55 = FixedSizeArray100.getKeypathToElement(index: 55) -let kp56 = FixedSizeArray100.getKeypathToElement(index: 56) -let kp57 = FixedSizeArray100.getKeypathToElement(index: 57) -let kp58 = FixedSizeArray100.getKeypathToElement(index: 58) -let kp59 = FixedSizeArray100.getKeypathToElement(index: 59) -let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) -let kp61 = FixedSizeArray100.getKeypathToElement(index: 61) -let kp62 = FixedSizeArray100.getKeypathToElement(index: 62) -let kp63 = FixedSizeArray100.getKeypathToElement(index: 63) -let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) -let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) -let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) -let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) -let kp68 = FixedSizeArray100.getKeypathToElement(index: 68) -let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) -let kp70 = FixedSizeArray100.getKeypathToElement(index: 70) -let kp71 = FixedSizeArray100.getKeypathToElement(index: 71) -let kp72 = FixedSizeArray100.getKeypathToElement(index: 72) -let kp73 = FixedSizeArray100.getKeypathToElement(index: 73) -let kp74 = FixedSizeArray100.getKeypathToElement(index: 74) -let kp75 = FixedSizeArray100.getKeypathToElement(index: 75) -let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) -let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) -let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) -let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) -let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) -let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) -let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) -let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) -let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) -let kp85 = FixedSizeArray100.getKeypathToElement(index: 85) -let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) -let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) -let kp88 = FixedSizeArray100.getKeypathToElement(index: 88) -let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) -let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) -let kp91 = FixedSizeArray100.getKeypathToElement(index: 91) -let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) -let kp93 = FixedSizeArray100.getKeypathToElement(index: 93) -let kp94 = FixedSizeArray100.getKeypathToElement(index: 94) -let kp95 = FixedSizeArray100.getKeypathToElement(index: 95) -let kp96 = FixedSizeArray100.getKeypathToElement(index: 96) -let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) -let kp98 = FixedSizeArray100.getKeypathToElement(index: 98) -let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) +let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) +let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) +let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) +let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) +let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) +let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) +let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) +let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) +let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) +let kp9 = FixedSizeArray100.getKeypathToElement(index: 9) +let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) +let kp11 = FixedSizeArray100.getKeypathToElement(index: 11) +let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) +let kp13 = FixedSizeArray100.getKeypathToElement(index: 13) +let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) +let kp15 = FixedSizeArray100.getKeypathToElement(index: 15) +let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) +let kp17 = FixedSizeArray100.getKeypathToElement(index: 17) +let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) +let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) +let kp20 = FixedSizeArray100.getKeypathToElement(index: 20) +let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) +let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) +let kp23 = FixedSizeArray100.getKeypathToElement(index: 23) +let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) +let kp25 = FixedSizeArray100.getKeypathToElement(index: 25) +let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) +let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) +let kp28 = FixedSizeArray100.getKeypathToElement(index: 28) +let kp29 = FixedSizeArray100.getKeypathToElement(index: 29) +let kp30 = FixedSizeArray100.getKeypathToElement(index: 30) +let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) +let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) +let kp33 = FixedSizeArray100.getKeypathToElement(index: 33) +let kp34 = FixedSizeArray100.getKeypathToElement(index: 34) +let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) +let kp36 = FixedSizeArray100.getKeypathToElement(index: 36) +let kp37 = FixedSizeArray100.getKeypathToElement(index: 37) +let kp38 = FixedSizeArray100.getKeypathToElement(index: 38) +let kp39 = FixedSizeArray100.getKeypathToElement(index: 39) +let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) +let kp41 = FixedSizeArray100.getKeypathToElement(index: 41) +let kp42 = FixedSizeArray100.getKeypathToElement(index: 42) +let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) +let kp44 = FixedSizeArray100.getKeypathToElement(index: 44) +let kp45 = FixedSizeArray100.getKeypathToElement(index: 45) +let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) +let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) +let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) +let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) +let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) +let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) +let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) +let kp53 = FixedSizeArray100.getKeypathToElement(index: 53) +let kp54 = FixedSizeArray100.getKeypathToElement(index: 54) +let kp55 = FixedSizeArray100.getKeypathToElement(index: 55) +let kp56 = FixedSizeArray100.getKeypathToElement(index: 56) +let kp57 = FixedSizeArray100.getKeypathToElement(index: 57) +let kp58 = FixedSizeArray100.getKeypathToElement(index: 58) +let kp59 = FixedSizeArray100.getKeypathToElement(index: 59) +let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) +let kp61 = FixedSizeArray100.getKeypathToElement(index: 61) +let kp62 = FixedSizeArray100.getKeypathToElement(index: 62) +let kp63 = FixedSizeArray100.getKeypathToElement(index: 63) +let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) +let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) +let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) +let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) +let kp68 = FixedSizeArray100.getKeypathToElement(index: 68) +let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) +let kp70 = FixedSizeArray100.getKeypathToElement(index: 70) +let kp71 = FixedSizeArray100.getKeypathToElement(index: 71) +let kp72 = FixedSizeArray100.getKeypathToElement(index: 72) +let kp73 = FixedSizeArray100.getKeypathToElement(index: 73) +let kp74 = FixedSizeArray100.getKeypathToElement(index: 74) +let kp75 = FixedSizeArray100.getKeypathToElement(index: 75) +let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) +let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) +let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) +let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) +let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) +let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) +let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) +let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) +let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) +let kp85 = FixedSizeArray100.getKeypathToElement(index: 85) +let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) +let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) +let kp88 = FixedSizeArray100.getKeypathToElement(index: 88) +let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) +let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) +let kp91 = FixedSizeArray100.getKeypathToElement(index: 91) +let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) +let kp93 = FixedSizeArray100.getKeypathToElement(index: 93) +let kp94 = FixedSizeArray100.getKeypathToElement(index: 94) +let kp95 = FixedSizeArray100.getKeypathToElement(index: 95) +let kp96 = FixedSizeArray100.getKeypathToElement(index: 96) +let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) +let kp98 = FixedSizeArray100.getKeypathToElement(index: 98) +let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) -func initializeFixedSizeArray100() -> FixedSizeArray100 { - return FixedSizeArray100( +func initializeFixedSizeArray100() -> FixedSizeArray100 { + return FixedSizeArray100( element0: 0, element1: 1, element2: 2, @@ -800,7 +869,7 @@ func initializeFixedSizeArray100() -> FixedSizeArray100 { ) } -func setupKeyPathArray() -> [AnyKeyPath] { +func returnKeyPathArray() -> [AnyKeyPath] { var arrayOfKeyPaths = [kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9] arrayOfKeyPaths.append(contentsOf: [kp10, kp11, kp12, kp13, kp14, kp15, kp16, kp17, kp18, kp19]) arrayOfKeyPaths.append(contentsOf: [kp20, kp21, kp22, kp23, kp24, kp25, kp26, kp27, kp28, kp29]) @@ -811,40 +880,26 @@ func setupKeyPathArray() -> [AnyKeyPath] { arrayOfKeyPaths.append(contentsOf: [kp70, kp71, kp72, kp73, kp74, kp75, kp76, kp77, kp78, kp79]) arrayOfKeyPaths.append(contentsOf: [kp80, kp81, kp82, kp83, kp84, kp85, kp86, kp87, kp88, kp89]) arrayOfKeyPaths.append(contentsOf: [kp90, kp91, kp92, kp93, kp94, kp95, kp96, kp97, kp98, kp99]) - return arrayOfKeyPaths } @inline(never) -public func run_KeyPathPerformanceOfNestedStructs(n _: Int) { - let numberOfInstances = 250_000 - let expectedInt = 3 - var mainArray = [A]() - for _ in 0 ..< numberOfInstances { - let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedInt))))) - mainArray.append(instance) - } - let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1).appending(path: singleHopKeyPath2) - .appending(path: singleHopKeyPath3).appending(path: singleHopKeyPath4) +public func run_KeyPathNestedStructs(n _: Int) { var sum = 0 - - for el in mainArray { + for el in FixedSizeArrayHolder.shared.mainArrayForNestedStructs { sum += el[keyPath: appendedKeyPath] } - assert(sum == numberOfInstances * expectedInt) + assert(sum == numberOfIterationsForNestedStructs * expectedIntForNestedStructs) } -let numberOfIterations = 100_000 -let expectedSum = 1.6669792312063853e+24 // Based on 100,000 iterations. - // This is meant as a baseline, from a timing perspective, // for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance(). @inline(never) public func run_testDirectAccess(n _: Int) { - var sum = Double(0) - var fixedSizeArray100 = initializeFixedSizeArray100() - for t in 0 ..< numberOfIterations { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + arrayHolder.reset() + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 + for t in 0 ..< numberOfIterationsForDirectAccess { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100.element27 fixedSizeArray100.element81 += fixedSizeArray100.element49 / fixedSizeArray100.element9 @@ -871,22 +926,17 @@ public func run_testDirectAccess(n _: Int) { fixedSizeArray100.element26 += fixedSizeArray100.element65 + fixedSizeArray100.element65 fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100.element50 } - - let keyPathArray = setupKeyPathArray() - for kp in keyPathArray { - sum += fixedSizeArray100[keyPath: kp]! as! Double - } - assert(sum == expectedSum) + let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) + assert(sum > ElementType(0)) } // This measures read performance of keypaths. -// It has the same number of keypath reads as run_testKeyPathWritePerformance() has writes. @inline(never) public func run_testKeyPathReadPerformance(n _: Int) { - var sum = Double(0) - var fixedSizeArray100 = initializeFixedSizeArray100() - for t in 0 ..< numberOfIterations { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + arrayHolder.reset() + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 + for t in 0 ..< numberOfIterationsForKeyPathReadPerformance { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100[keyPath: kp99] fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100[keyPath: kp27] fixedSizeArray100.element81 += fixedSizeArray100.element49 / fixedSizeArray100[keyPath: kp9] @@ -913,22 +963,17 @@ public func run_testKeyPathReadPerformance(n _: Int) { fixedSizeArray100.element26 += fixedSizeArray100.element65 + fixedSizeArray100[keyPath: kp65] fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100[keyPath: kp50] } - - let keyPathArray = setupKeyPathArray() - for kp in keyPathArray { - sum += fixedSizeArray100[keyPath: kp]! as! Double - } - assert(sum == expectedSum) + let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) + assert(sum > ElementType(0)) } // This measures write performance of keypaths. -// It has the same number of keypath writes as run_testKeyPathReadPerformance() has reads. @inline(never) public func run_testKeyPathWritePerformance(n _: Int) { - var sum = Double(0) - var fixedSizeArray100 = initializeFixedSizeArray100() - for t in 0 ..< numberOfIterations { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + Double(t) + arrayHolder.reset() + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 + for t in 0 ..< numberOfIterationsForKeyPathWritePerformance { + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100.element99 fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100.element27 fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100.element9 @@ -955,10 +1000,6 @@ public func run_testKeyPathWritePerformance(n _: Int) { fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100.element65 fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100.element50 } - - let keyPathArray = setupKeyPathArray() - for kp in keyPathArray { - sum += fixedSizeArray100[keyPath: kp]! as! Double - } - assert(sum == expectedSum) + let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) + assert(sum > ElementType(0)) } From ea9984b2ed3afe6be27f345722eeaa5e7ad0c1c8 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 5 Aug 2022 14:33:20 -0600 Subject: [PATCH 03/18] Include n as a factor in the number of iterations. --- .../KeyPathPerformanceTests.swift | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 8796567406f31..44bf37151a2c4 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -39,10 +39,7 @@ public let benchmarks = [ ] // Number of iterations for each benchmark. -let numberOfIterationsForNestedStructs = 20000 -let numberOfIterationsForDirectAccess = 1_000_000 -let numberOfIterationsForKeyPathReadPerformance = 2000 -let numberOfIterationsForKeyPathWritePerformance = 12000 +let numberOfElementsForNestedStructs = 20000 // Make it easy to switch between primitives of different types (Int, Float, Double, etc.). typealias ElementType = Double @@ -78,7 +75,7 @@ class FixedSizeArrayHolder { // Setup Functions. public func setupKeyPathNestedStructs() { FixedSizeArrayHolder.shared.forceInstantiation() - for _ in 0 ..< numberOfIterationsForNestedStructs { + for _ in 0 ..< numberOfElementsForNestedStructs { let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedStructs))))) FixedSizeArrayHolder.shared.mainArrayForNestedStructs.append(instance) } @@ -884,21 +881,26 @@ func returnKeyPathArray() -> [AnyKeyPath] { } @inline(never) -public func run_KeyPathNestedStructs(n _: Int) { +public func run_KeyPathNestedStructs(n: Int) { var sum = 0 - for el in FixedSizeArrayHolder.shared.mainArrayForNestedStructs { - sum += el[keyPath: appendedKeyPath] + var index = 0 + let iterationMultipier = 200 + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.mainArrayForNestedStructs[index] + sum += element[keyPath: appendedKeyPath] + index = (index + 1) % FixedSizeArrayHolder.shared.mainArrayForNestedStructs.count } - assert(sum == numberOfIterationsForNestedStructs * expectedIntForNestedStructs) + assert(sum == iterationMultipier * n * expectedIntForNestedStructs) } // This is meant as a baseline, from a timing perspective, // for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance(). @inline(never) -public func run_testDirectAccess(n _: Int) { +public func run_testDirectAccess(n: Int) { arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - for t in 0 ..< numberOfIterationsForDirectAccess { + let iterationMultipier = 3000 + for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100.element27 @@ -932,10 +934,11 @@ public func run_testDirectAccess(n _: Int) { // This measures read performance of keypaths. @inline(never) -public func run_testKeyPathReadPerformance(n _: Int) { +public func run_testKeyPathReadPerformance(n: Int) { arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - for t in 0 ..< numberOfIterationsForKeyPathReadPerformance { + let iterationMultipier = 25 + for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100[keyPath: kp99] fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100[keyPath: kp27] @@ -969,10 +972,11 @@ public func run_testKeyPathReadPerformance(n _: Int) { // This measures write performance of keypaths. @inline(never) -public func run_testKeyPathWritePerformance(n _: Int) { +public func run_testKeyPathWritePerformance(n: Int) { arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - for t in 0 ..< numberOfIterationsForKeyPathWritePerformance { + let iterationMultipier = 150 + for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100.element99 fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100.element27 From 578ce7da959a36bc5824261763fd0949bc193720 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Mon, 8 Aug 2022 10:32:15 -0600 Subject: [PATCH 04/18] Increased number of iterations for KeyPathDirectAccess by a factor of 25. --- benchmark/single-source/KeyPathPerformanceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 44bf37151a2c4..44f7044f6e6ea 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -899,7 +899,7 @@ public func run_KeyPathNestedStructs(n: Int) { public func run_testDirectAccess(n: Int) { arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - let iterationMultipier = 3000 + let iterationMultipier = 75000 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 From 14ef45a67d9c252de39a46fa0dec5211081d7952 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Mon, 8 Aug 2022 14:17:25 -0600 Subject: [PATCH 05/18] One last tweak to the number of iterations on testDirectAccess to get them above 20 us. --- benchmark/single-source/KeyPathPerformanceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 44f7044f6e6ea..ec4a16817b979 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -899,7 +899,7 @@ public func run_KeyPathNestedStructs(n: Int) { public func run_testDirectAccess(n: Int) { arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - let iterationMultipier = 75000 + let iterationMultipier = 125000 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 From 2efa8eb26284d07138da87a2423d0a515c7dce68 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Thu, 11 Aug 2022 15:48:31 -0600 Subject: [PATCH 06/18] Made revisions based on feedback. Added three new benchmarks. --- .../KeyPathPerformanceTests.swift | 744 +++++++++++++----- 1 file changed, 542 insertions(+), 202 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index ec4a16817b979..b1a93fd464031 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -21,7 +21,7 @@ public let benchmarks = [ BenchmarkInfo( name: "KeyPathDirectAccess", runFunction: run_testDirectAccess, - tags: [.validation, .api], + tags: [.validation, .api, .skip], setUpFunction: setupSingleton ), BenchmarkInfo( @@ -36,53 +36,58 @@ public let benchmarks = [ tags: [.validation, .api], setUpFunction: setupSingleton ), + BenchmarkInfo( + name: "KeyPathInlined", + runFunction: run_testKeyPathsInlined, + tags: [.validation, .api], + setUpFunction: setupSingleton + ), + BenchmarkInfo( + name: "KeyPathsNotInlined", + runFunction: run_testKeyPathsNotInlined, + tags: [.validation, .api], + setUpFunction: setupSingleton + ), + BenchmarkInfo( + name: "KeyPathsSmallStruct", + runFunction: run_testKeyPathSmallStruct, + tags: [.validation, .api], + setUpFunction: setupSingleton + ), ] -// Number of iterations for each benchmark. let numberOfElementsForNestedStructs = 20000 +let expectedIntForNestedStructs = 3 // Make it easy to switch between primitives of different types (Int, Float, Double, etc.). typealias ElementType = Double -// Variables used across the benchmarks -let expectedIntForNestedStructs = 3 -let arrayHolder = FixedSizeArrayHolder() - -let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1) - .appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3) - .appending(path: singleHopKeyPath4) - // The purpose of this class is to hold arrays used during the course of the benchmarks. // This is to avoid storing them in the global scope, which can slow things down. -// We force instantiation in the setUpFunction so that we exclude the setup time from the benchmark time itself, -// otherwise the instance would be lazily instantiated, and thereby add to the benchmark time. class FixedSizeArrayHolder { var fixedSizeArray100: FixedSizeArray100 + var fixedSizeArray10: FixedSizeArray10 var mainArrayForNestedStructs: [A] + static let shared = FixedSizeArrayHolder() init() { fixedSizeArray100 = initializeFixedSizeArray100() + fixedSizeArray10 = initializeFixedSizeArray10() mainArrayForNestedStructs = [A]() } - - func reset() { - fixedSizeArray100 = initializeFixedSizeArray100() - } - - func forceInstantiation() {} } // Setup Functions. public func setupKeyPathNestedStructs() { - FixedSizeArrayHolder.shared.forceInstantiation() + let holder = FixedSizeArrayHolder.shared for _ in 0 ..< numberOfElementsForNestedStructs { let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedStructs))))) - FixedSizeArrayHolder.shared.mainArrayForNestedStructs.append(instance) + holder.mainArrayForNestedStructs.append(instance) } } public func setupSingleton() { - FixedSizeArrayHolder.shared.forceInstantiation() + blackHole(FixedSizeArrayHolder.shared) } func computeSumOfFixedSizeArray100(fixedSizeArray100: inout FixedSizeArray100) @@ -99,6 +104,20 @@ func computeSumOfFixedSizeArray100(fixedSizeArray100: inout FixedSizeArray100) + -> ElementType +{ + var sum = ElementType(0) + withUnsafeBytes(of: fixedSizeArray10) { + var pointer = $0.baseAddress.unsafelyUnwrapped + for _ in 0 ..< fixedSizeArray10.count { + sum += pointer.assumingMemoryBound(to: ElementType.self).pointee + pointer = pointer.advanced(by: MemoryLayout.size) + } + } + return sum +} + // Used for run_KeyPathNestedStructs(). struct A { var a: Int @@ -124,12 +143,6 @@ struct E { var e: Int } -let singleHopKeyPath0: WritableKeyPath = \A.b -let singleHopKeyPath1: WritableKeyPath = \B.c -let singleHopKeyPath2: WritableKeyPath = \C.d -let singleHopKeyPath3: WritableKeyPath = \D.e -let singleHopKeyPath4: WritableKeyPath = \E.e - // Used for run_KeyPathFixedSizeArrayAccess() and run_testDirectAccess(). struct FixedSizeArray100: Sequence, IteratorProtocol { let count: Int = 100 @@ -660,106 +673,101 @@ struct FixedSizeArray100: Sequence, IteratorProtocol { } } -let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) -let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) -let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) -let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) -let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) -let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) -let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) -let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) -let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) -let kp9 = FixedSizeArray100.getKeypathToElement(index: 9) -let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) -let kp11 = FixedSizeArray100.getKeypathToElement(index: 11) -let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) -let kp13 = FixedSizeArray100.getKeypathToElement(index: 13) -let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) -let kp15 = FixedSizeArray100.getKeypathToElement(index: 15) -let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) -let kp17 = FixedSizeArray100.getKeypathToElement(index: 17) -let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) -let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) -let kp20 = FixedSizeArray100.getKeypathToElement(index: 20) -let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) -let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) -let kp23 = FixedSizeArray100.getKeypathToElement(index: 23) -let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) -let kp25 = FixedSizeArray100.getKeypathToElement(index: 25) -let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) -let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) -let kp28 = FixedSizeArray100.getKeypathToElement(index: 28) -let kp29 = FixedSizeArray100.getKeypathToElement(index: 29) -let kp30 = FixedSizeArray100.getKeypathToElement(index: 30) -let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) -let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) -let kp33 = FixedSizeArray100.getKeypathToElement(index: 33) -let kp34 = FixedSizeArray100.getKeypathToElement(index: 34) -let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) -let kp36 = FixedSizeArray100.getKeypathToElement(index: 36) -let kp37 = FixedSizeArray100.getKeypathToElement(index: 37) -let kp38 = FixedSizeArray100.getKeypathToElement(index: 38) -let kp39 = FixedSizeArray100.getKeypathToElement(index: 39) -let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) -let kp41 = FixedSizeArray100.getKeypathToElement(index: 41) -let kp42 = FixedSizeArray100.getKeypathToElement(index: 42) -let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) -let kp44 = FixedSizeArray100.getKeypathToElement(index: 44) -let kp45 = FixedSizeArray100.getKeypathToElement(index: 45) -let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) -let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) -let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) -let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) -let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) -let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) -let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) -let kp53 = FixedSizeArray100.getKeypathToElement(index: 53) -let kp54 = FixedSizeArray100.getKeypathToElement(index: 54) -let kp55 = FixedSizeArray100.getKeypathToElement(index: 55) -let kp56 = FixedSizeArray100.getKeypathToElement(index: 56) -let kp57 = FixedSizeArray100.getKeypathToElement(index: 57) -let kp58 = FixedSizeArray100.getKeypathToElement(index: 58) -let kp59 = FixedSizeArray100.getKeypathToElement(index: 59) -let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) -let kp61 = FixedSizeArray100.getKeypathToElement(index: 61) -let kp62 = FixedSizeArray100.getKeypathToElement(index: 62) -let kp63 = FixedSizeArray100.getKeypathToElement(index: 63) -let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) -let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) -let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) -let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) -let kp68 = FixedSizeArray100.getKeypathToElement(index: 68) -let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) -let kp70 = FixedSizeArray100.getKeypathToElement(index: 70) -let kp71 = FixedSizeArray100.getKeypathToElement(index: 71) -let kp72 = FixedSizeArray100.getKeypathToElement(index: 72) -let kp73 = FixedSizeArray100.getKeypathToElement(index: 73) -let kp74 = FixedSizeArray100.getKeypathToElement(index: 74) -let kp75 = FixedSizeArray100.getKeypathToElement(index: 75) -let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) -let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) -let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) -let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) -let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) -let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) -let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) -let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) -let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) -let kp85 = FixedSizeArray100.getKeypathToElement(index: 85) -let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) -let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) -let kp88 = FixedSizeArray100.getKeypathToElement(index: 88) -let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) -let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) -let kp91 = FixedSizeArray100.getKeypathToElement(index: 91) -let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) -let kp93 = FixedSizeArray100.getKeypathToElement(index: 93) -let kp94 = FixedSizeArray100.getKeypathToElement(index: 94) -let kp95 = FixedSizeArray100.getKeypathToElement(index: 95) -let kp96 = FixedSizeArray100.getKeypathToElement(index: 96) -let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) -let kp98 = FixedSizeArray100.getKeypathToElement(index: 98) -let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) +struct FixedSizeArray10: Sequence, IteratorProtocol { + let count: Int = 10 + + public init( + element0: Element, + element1: Element, + element2: Element, + element3: Element, + element4: Element, + element5: Element, + element6: Element, + element7: Element, + element8: Element, + element9: Element + + ) { + self.element0 = element0 + self.element1 = element1 + self.element2 = element2 + self.element3 = element3 + self.element4 = element4 + self.element5 = element5 + self.element6 = element6 + self.element7 = element7 + self.element8 = element8 + self.element9 = element9 + } + + var element0: Element + var element1: Element + var element2: Element + var element3: Element + var element4: Element + var element5: Element + var element6: Element + var element7: Element + var element8: Element + var element9: Element + + static func getKeypathToElement(index: Int) + -> WritableKeyPath, Element> + { + switch index { + case 0: + return \FixedSizeArray10.element0 + case 1: + return \FixedSizeArray10.element1 + case 2: + return \FixedSizeArray10.element2 + case 3: + return \FixedSizeArray10.element3 + case 4: + return \FixedSizeArray10.element4 + case 5: + return \FixedSizeArray10.element5 + case 6: + return \FixedSizeArray10.element6 + case 7: + return \FixedSizeArray10.element7 + case 8: + return \FixedSizeArray10.element8 + case 9: + return \FixedSizeArray10.element9 + + default: + fatalError() + } + } + + // Conformance to Iterator. + mutating func next() -> Element? { + var iter_count = 0 + if iter_count == count { + return nil + } else { + defer { iter_count += 1 } + return self[keyPath: FixedSizeArray10.getKeypathToElement(index: iter_count)] + } + } +} + +func initializeFixedSizeArray10() -> FixedSizeArray10 { + return FixedSizeArray10( + element0: 0, + element1: 1, + element2: 2, + element3: 3, + element4: 4, + element5: 5, + element6: 6, + element7: 7, + element8: 8, + element9: 9 + ) +} func initializeFixedSizeArray100() -> FixedSizeArray100 { return FixedSizeArray100( @@ -866,40 +874,38 @@ func initializeFixedSizeArray100() -> FixedSizeArray100 { ) } -func returnKeyPathArray() -> [AnyKeyPath] { - var arrayOfKeyPaths = [kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9] - arrayOfKeyPaths.append(contentsOf: [kp10, kp11, kp12, kp13, kp14, kp15, kp16, kp17, kp18, kp19]) - arrayOfKeyPaths.append(contentsOf: [kp20, kp21, kp22, kp23, kp24, kp25, kp26, kp27, kp28, kp29]) - arrayOfKeyPaths.append(contentsOf: [kp30, kp31, kp32, kp33, kp34, kp35, kp36, kp37, kp38, kp39]) - arrayOfKeyPaths.append(contentsOf: [kp40, kp41, kp42, kp43, kp44, kp45, kp46, kp47, kp48, kp49]) - arrayOfKeyPaths.append(contentsOf: [kp50, kp51, kp52, kp53, kp54, kp55, kp56, kp57, kp58, kp59]) - arrayOfKeyPaths.append(contentsOf: [kp60, kp61, kp62, kp63, kp64, kp65, kp66, kp67, kp68, kp69]) - arrayOfKeyPaths.append(contentsOf: [kp70, kp71, kp72, kp73, kp74, kp75, kp76, kp77, kp78, kp79]) - arrayOfKeyPaths.append(contentsOf: [kp80, kp81, kp82, kp83, kp84, kp85, kp86, kp87, kp88, kp89]) - arrayOfKeyPaths.append(contentsOf: [kp90, kp91, kp92, kp93, kp94, kp95, kp96, kp97, kp98, kp99]) - return arrayOfKeyPaths -} - @inline(never) public func run_KeyPathNestedStructs(n: Int) { var sum = 0 var index = 0 let iterationMultipier = 200 + + let singleHopKeyPath0: WritableKeyPath = \A.b + let singleHopKeyPath1: WritableKeyPath = \B.c + let singleHopKeyPath2: WritableKeyPath = \C.d + let singleHopKeyPath3: WritableKeyPath = \D.e + let singleHopKeyPath4: WritableKeyPath = \E.e + + let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1) + .appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3) + .appending(path: singleHopKeyPath4) + + let elementCount = FixedSizeArrayHolder.shared.mainArrayForNestedStructs.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.mainArrayForNestedStructs[index] - sum += element[keyPath: appendedKeyPath] - index = (index + 1) % FixedSizeArrayHolder.shared.mainArrayForNestedStructs.count + sum += element[keyPath: identity(appendedKeyPath)] + index = (index + 1) % elementCount } - assert(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedStructs) } // This is meant as a baseline, from a timing perspective, // for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance(). +// It's currently set to ".skip", but is useful for comparing the performance between keypath operations and direct dot-notation. @inline(never) public func run_testDirectAccess(n: Int) { - arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - let iterationMultipier = 125000 + let iterationMultipier = 125_000 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100.element99 @@ -929,81 +935,415 @@ public func run_testDirectAccess(n: Int) { fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100.element50 } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) - assert(sum > ElementType(0)) + check(sum > ElementType(0)) } -// This measures read performance of keypaths. @inline(never) public func run_testKeyPathReadPerformance(n: Int) { - arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 25 + + let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) + let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) + let kp09 = FixedSizeArray100.getKeypathToElement(index: 09) + let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) + let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) + let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) + let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) + let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) + let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) + let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) + let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) + let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) + let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) + let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) + let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) + let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) + let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) + let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) + let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) + let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) + let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) + let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) + let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) + let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) + let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) + for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100.element46 += fixedSizeArray100.element63 - fixedSizeArray100[keyPath: kp99] - fixedSizeArray100.element43 += fixedSizeArray100.element39 * fixedSizeArray100[keyPath: kp27] - fixedSizeArray100.element81 += fixedSizeArray100.element49 / fixedSizeArray100[keyPath: kp9] - fixedSizeArray100.element31 += fixedSizeArray100.element90 + fixedSizeArray100[keyPath: kp84] - fixedSizeArray100.element35 += fixedSizeArray100.element6 - fixedSizeArray100[keyPath: kp22] - fixedSizeArray100.element14 += fixedSizeArray100.element67 * fixedSizeArray100[keyPath: kp82] - fixedSizeArray100.element51 += fixedSizeArray100.element40 / fixedSizeArray100[keyPath: kp86] - fixedSizeArray100.element24 += fixedSizeArray100.element23 + fixedSizeArray100[keyPath: kp49] - fixedSizeArray100.element90 += fixedSizeArray100.element95 - fixedSizeArray100[keyPath: kp18] - fixedSizeArray100.element80 += fixedSizeArray100.element45 * fixedSizeArray100[keyPath: kp97] - fixedSizeArray100.element47 += fixedSizeArray100.element65 / fixedSizeArray100[keyPath: kp69] - fixedSizeArray100.element92 += fixedSizeArray100.element80 + fixedSizeArray100[keyPath: kp76] - fixedSizeArray100.element78 += fixedSizeArray100.element32 - fixedSizeArray100[keyPath: kp32] - fixedSizeArray100.element79 += fixedSizeArray100.element59 * fixedSizeArray100[keyPath: kp52] - fixedSizeArray100.element46 += fixedSizeArray100.element60 / fixedSizeArray100[keyPath: kp24] - fixedSizeArray100.element64 += fixedSizeArray100.element41 + fixedSizeArray100[keyPath: kp87] - fixedSizeArray100.element65 += fixedSizeArray100.element72 - fixedSizeArray100[keyPath: kp67] - fixedSizeArray100.element19 += fixedSizeArray100.element81 * fixedSizeArray100[keyPath: kp65] - fixedSizeArray100.element66 += fixedSizeArray100.element55 / fixedSizeArray100[keyPath: kp43] - fixedSizeArray100.element10 += fixedSizeArray100.element52 + fixedSizeArray100[keyPath: kp12] - fixedSizeArray100.element81 += fixedSizeArray100.element50 - fixedSizeArray100[keyPath: kp21] - fixedSizeArray100.element16 += fixedSizeArray100.element80 * fixedSizeArray100[keyPath: kp77] - fixedSizeArray100.element6 += fixedSizeArray100.element62 / fixedSizeArray100[keyPath: kp40] - fixedSizeArray100.element26 += fixedSizeArray100.element65 + fixedSizeArray100[keyPath: kp65] - fixedSizeArray100.element83 += fixedSizeArray100.element78 - fixedSizeArray100[keyPath: kp50] + fixedSizeArray100.element46 += fixedSizeArray100 + .element63 - fixedSizeArray100[keyPath: identity(kp99)] + fixedSizeArray100.element43 += fixedSizeArray100 + .element39 * fixedSizeArray100[keyPath: identity(kp27)] + fixedSizeArray100.element81 += fixedSizeArray100 + .element49 / fixedSizeArray100[keyPath: identity(kp09)] + fixedSizeArray100.element31 += fixedSizeArray100 + .element90 + fixedSizeArray100[keyPath: identity(kp84)] + fixedSizeArray100.element35 += fixedSizeArray100 + .element6 - fixedSizeArray100[keyPath: identity(kp22)] + fixedSizeArray100.element14 += fixedSizeArray100 + .element67 * fixedSizeArray100[keyPath: identity(kp82)] + fixedSizeArray100.element51 += fixedSizeArray100 + .element40 / fixedSizeArray100[keyPath: identity(kp86)] + fixedSizeArray100.element24 += fixedSizeArray100 + .element23 + fixedSizeArray100[keyPath: identity(kp49)] + fixedSizeArray100.element90 += fixedSizeArray100 + .element95 - fixedSizeArray100[keyPath: identity(kp18)] + fixedSizeArray100.element80 += fixedSizeArray100 + .element45 * fixedSizeArray100[keyPath: identity(kp97)] + fixedSizeArray100.element47 += fixedSizeArray100 + .element65 / fixedSizeArray100[keyPath: identity(kp69)] + fixedSizeArray100.element92 += fixedSizeArray100 + .element80 + fixedSizeArray100[keyPath: identity(kp76)] + fixedSizeArray100.element78 += fixedSizeArray100 + .element32 - fixedSizeArray100[keyPath: identity(kp32)] + fixedSizeArray100.element79 += fixedSizeArray100 + .element59 * fixedSizeArray100[keyPath: identity(kp52)] + fixedSizeArray100.element46 += fixedSizeArray100 + .element60 / fixedSizeArray100[keyPath: identity(kp24)] + fixedSizeArray100.element64 += fixedSizeArray100 + .element41 + fixedSizeArray100[keyPath: identity(kp87)] + fixedSizeArray100.element65 += fixedSizeArray100 + .element72 - fixedSizeArray100[keyPath: identity(kp67)] + fixedSizeArray100.element19 += fixedSizeArray100 + .element81 * fixedSizeArray100[keyPath: identity(kp65)] + fixedSizeArray100.element66 += fixedSizeArray100 + .element55 / fixedSizeArray100[keyPath: identity(kp43)] + fixedSizeArray100.element10 += fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: identity(kp12)] + fixedSizeArray100.element81 += fixedSizeArray100 + .element50 - fixedSizeArray100[keyPath: identity(kp21)] + fixedSizeArray100.element16 += fixedSizeArray100 + .element80 * fixedSizeArray100[keyPath: identity(kp77)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element62 / fixedSizeArray100[keyPath: identity(kp40)] + fixedSizeArray100.element26 += fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: identity(kp60)] + fixedSizeArray100.element83 += fixedSizeArray100 + .element78 - fixedSizeArray100[keyPath: identity(kp50)] } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) - assert(sum > ElementType(0)) + check(sum > ElementType(0)) } // This measures write performance of keypaths. @inline(never) public func run_testKeyPathWritePerformance(n: Int) { - arrayHolder.reset() var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 150 + + let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) + let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) + let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) + let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) + let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) + let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) + let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) + let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) + let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) + let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) + let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) + let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) + let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) + let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) + let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) + let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) + let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) + let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) + let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) + let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) + let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) + let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) + let kp06 = FixedSizeArray100.getKeypathToElement(index: 06) + let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) + let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) + for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100.element99 - fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100.element27 - fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100.element9 - fixedSizeArray100[keyPath: kp31] += fixedSizeArray100.element90 + fixedSizeArray100.element84 - fixedSizeArray100[keyPath: kp35] += fixedSizeArray100.element6 - fixedSizeArray100.element22 - fixedSizeArray100[keyPath: kp14] += fixedSizeArray100.element67 * fixedSizeArray100.element82 - fixedSizeArray100[keyPath: kp51] += fixedSizeArray100.element40 / fixedSizeArray100.element86 - fixedSizeArray100[keyPath: kp24] += fixedSizeArray100.element23 + fixedSizeArray100.element49 - fixedSizeArray100[keyPath: kp90] += fixedSizeArray100.element95 - fixedSizeArray100.element18 - fixedSizeArray100[keyPath: kp80] += fixedSizeArray100.element45 * fixedSizeArray100.element97 - fixedSizeArray100[keyPath: kp47] += fixedSizeArray100.element65 / fixedSizeArray100.element69 - fixedSizeArray100[keyPath: kp92] += fixedSizeArray100.element80 + fixedSizeArray100.element76 - fixedSizeArray100[keyPath: kp78] += fixedSizeArray100.element32 - fixedSizeArray100.element32 - fixedSizeArray100[keyPath: kp79] += fixedSizeArray100.element59 * fixedSizeArray100.element52 - fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element60 / fixedSizeArray100.element24 - fixedSizeArray100[keyPath: kp64] += fixedSizeArray100.element41 + fixedSizeArray100.element87 - fixedSizeArray100[keyPath: kp65] += fixedSizeArray100.element72 - fixedSizeArray100.element67 - fixedSizeArray100[keyPath: kp19] += fixedSizeArray100.element81 * fixedSizeArray100.element65 - fixedSizeArray100[keyPath: kp66] += fixedSizeArray100.element55 / fixedSizeArray100.element43 - fixedSizeArray100[keyPath: kp10] += fixedSizeArray100.element52 + fixedSizeArray100.element12 - fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element50 - fixedSizeArray100.element21 - fixedSizeArray100[keyPath: kp16] += fixedSizeArray100.element80 * fixedSizeArray100.element77 - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100.element62 / fixedSizeArray100.element40 - fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100.element65 - fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100.element50 + fixedSizeArray100[keyPath: identity(kp46)] += fixedSizeArray100.element63 - fixedSizeArray100 + .element99 + fixedSizeArray100[keyPath: identity(kp43)] += fixedSizeArray100.element39 * fixedSizeArray100 + .element27 + fixedSizeArray100[keyPath: identity(kp81)] += fixedSizeArray100.element49 / fixedSizeArray100 + .element9 + fixedSizeArray100[keyPath: identity(kp31)] += fixedSizeArray100.element90 + fixedSizeArray100 + .element84 + fixedSizeArray100[keyPath: identity(kp35)] += fixedSizeArray100.element6 - fixedSizeArray100 + .element22 + fixedSizeArray100[keyPath: identity(kp14)] += fixedSizeArray100.element67 * fixedSizeArray100 + .element82 + fixedSizeArray100[keyPath: identity(kp51)] += fixedSizeArray100.element40 / fixedSizeArray100 + .element86 + fixedSizeArray100[keyPath: identity(kp24)] += fixedSizeArray100.element23 + fixedSizeArray100 + .element49 + fixedSizeArray100[keyPath: identity(kp90)] += fixedSizeArray100.element95 - fixedSizeArray100 + .element18 + fixedSizeArray100[keyPath: identity(kp80)] += fixedSizeArray100.element45 * fixedSizeArray100 + .element97 + fixedSizeArray100[keyPath: identity(kp47)] += fixedSizeArray100.element65 / fixedSizeArray100 + .element69 + fixedSizeArray100[keyPath: identity(kp92)] += fixedSizeArray100.element80 + fixedSizeArray100 + .element76 + fixedSizeArray100[keyPath: identity(kp78)] += fixedSizeArray100.element32 - fixedSizeArray100 + .element32 + fixedSizeArray100[keyPath: identity(kp79)] += fixedSizeArray100.element59 * fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: identity(kp48)] += fixedSizeArray100.element60 / fixedSizeArray100 + .element24 + fixedSizeArray100[keyPath: identity(kp64)] += fixedSizeArray100.element41 + fixedSizeArray100 + .element87 + fixedSizeArray100[keyPath: identity(kp65)] += fixedSizeArray100.element72 - fixedSizeArray100 + .element67 + fixedSizeArray100[keyPath: identity(kp19)] += fixedSizeArray100.element81 * fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: identity(kp66)] += fixedSizeArray100.element55 / fixedSizeArray100 + .element43 + fixedSizeArray100[keyPath: identity(kp10)] += fixedSizeArray100.element52 + fixedSizeArray100 + .element12 + fixedSizeArray100[keyPath: identity(kp89)] += fixedSizeArray100.element50 - fixedSizeArray100 + .element21 + fixedSizeArray100[keyPath: identity(kp16)] += fixedSizeArray100.element80 * fixedSizeArray100 + .element77 + fixedSizeArray100[keyPath: identity(kp06)] += fixedSizeArray100.element62 / fixedSizeArray100 + .element40 + fixedSizeArray100[keyPath: identity(kp26)] += fixedSizeArray100.element65 + fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: identity(kp83)] += fixedSizeArray100.element78 - fixedSizeArray100 + .element50 + } + let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) + check(sum > ElementType(0)) +} + +/* + The two benchmarks below demonstrate an issue where, after a certain point, a collection of + nearby keypaths no longer get inlined by the compiler. Namely, if the memory addresses end + up being too far apart, the inlining process won't proceed. This divergence occurs during + one of the SIL optimization passes. The "@inline(never)" directive has been intentionally + omitted, as well as wrapping the KeyPaths in the identity() function in order to highlight + this issue. These two benchmarks will only differ in performance when compiled in release + mode, namely, "swift build --configuration release". Inlining will happen if "lastKeyPathIndex" + is set to any integer from 0 to 13, but will not happen if set to 14 to 49. + */ + +public func run_testKeyPathsInlined(n: Int) { + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 + let iterationMultipier = 1250 + + let lastKeyPathIndex = 9 + let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) + let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) + let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) + let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) + let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) + let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) + let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) + let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) + let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) + let kp9 = FixedSizeArray100.getKeypathToElement(index: lastKeyPathIndex) + + for t in 1 ... iterationMultipier * n { + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp1] + ElementType(t) + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp3] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp4] + fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp6] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp7] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp6] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp3] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp8] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp7] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp2] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp6] + fixedSizeArray100[keyPath: kp8] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp4] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp1] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp1] + + fixedSizeArray100[keyPath: kp5] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp3] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp1] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp0] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp5] + fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp8] + + fixedSizeArray100[keyPath: kp0] } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) - assert(sum > ElementType(0)) + + blackHole(sum) +} + +public func run_testKeyPathsNotInlined(n: Int) { + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 + let iterationMultipier = 100 + + let lastKeyPathIndex = 44 + let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) + let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) + let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) + let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) + let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) + let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) + let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) + let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) + let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) + let kp9 = FixedSizeArray100.getKeypathToElement(index: lastKeyPathIndex) + + for t in 1 ... iterationMultipier * n { + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp1] + ElementType(t) + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp3] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp4] + fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp6] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp7] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp6] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp3] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp8] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp7] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp9] + fixedSizeArray100[keyPath: kp2] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp6] + fixedSizeArray100[keyPath: kp8] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp9] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp4] + fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp1] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp1] + + fixedSizeArray100[keyPath: kp5] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp3] + fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp2] + fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp1] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + + fixedSizeArray100[keyPath: kp7] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp2] + + fixedSizeArray100[keyPath: kp0] + fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + + fixedSizeArray100[keyPath: kp5] + fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp8] + + fixedSizeArray100[keyPath: kp0] + } + let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) + blackHole(sum) +} + + +// Intended to showcase the difference in performance between 10 and 100-element structs. +@inline(never) +public func run_testKeyPathSmallStruct(n: Int) { + var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray10 + let iterationMultipier = 25 + + let kp0 = FixedSizeArray10.getKeypathToElement(index: 0) + let kp1 = FixedSizeArray10.getKeypathToElement(index: 1) + let kp2 = FixedSizeArray10.getKeypathToElement(index: 2) + let kp3 = FixedSizeArray10.getKeypathToElement(index: 3) + let kp4 = FixedSizeArray10.getKeypathToElement(index: 4) + let kp5 = FixedSizeArray10.getKeypathToElement(index: 5) + let kp6 = FixedSizeArray10.getKeypathToElement(index: 6) + let kp7 = FixedSizeArray10.getKeypathToElement(index: 7) + let kp8 = FixedSizeArray10.getKeypathToElement(index: 8) + let kp9 = FixedSizeArray10.getKeypathToElement(index: 9) + + for t in 1 ... iterationMultipier * n { + fixedSizeArray100.element0 += fixedSizeArray100.element1 + ElementType(t) + fixedSizeArray100.element6 += fixedSizeArray100 + .element3 - fixedSizeArray100[keyPath: identity(kp9)] + fixedSizeArray100.element3 += fixedSizeArray100 + .element9 * fixedSizeArray100[keyPath: identity(kp7)] + fixedSizeArray100.element1 += fixedSizeArray100 + .element9 / fixedSizeArray100[keyPath: identity(kp9)] + fixedSizeArray100.element1 += fixedSizeArray100 + .element0 + fixedSizeArray100[keyPath: identity(kp4)] + fixedSizeArray100.element5 += fixedSizeArray100 + .element6 - fixedSizeArray100[keyPath: identity(kp2)] + fixedSizeArray100.element4 += fixedSizeArray100 + .element7 * fixedSizeArray100[keyPath: identity(kp2)] + fixedSizeArray100.element1 += fixedSizeArray100 + .element0 / fixedSizeArray100[keyPath: identity(kp6)] + fixedSizeArray100.element4 += fixedSizeArray100 + .element3 + fixedSizeArray100[keyPath: identity(kp9)] + fixedSizeArray100.element0 += fixedSizeArray100 + .element5 - fixedSizeArray100[keyPath: identity(kp8)] + fixedSizeArray100.element0 += fixedSizeArray100 + .element5 * fixedSizeArray100[keyPath: identity(kp7)] + fixedSizeArray100.element7 += fixedSizeArray100 + .element5 / fixedSizeArray100[keyPath: identity(kp9)] + fixedSizeArray100.element2 += fixedSizeArray100 + .element0 + fixedSizeArray100[keyPath: identity(kp6)] + fixedSizeArray100.element8 += fixedSizeArray100 + .element2 - fixedSizeArray100[keyPath: identity(kp2)] + fixedSizeArray100.element9 += fixedSizeArray100 + .element9 * fixedSizeArray100[keyPath: identity(kp2)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element0 / fixedSizeArray100[keyPath: identity(kp4)] + fixedSizeArray100.element4 += fixedSizeArray100 + .element1 + fixedSizeArray100[keyPath: identity(kp7)] + fixedSizeArray100.element5 += fixedSizeArray100 + .element2 - fixedSizeArray100[keyPath: identity(kp7)] + fixedSizeArray100.element9 += fixedSizeArray100 + .element1 * fixedSizeArray100[keyPath: identity(kp5)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element5 / fixedSizeArray100[keyPath: identity(kp3)] + fixedSizeArray100.element0 += fixedSizeArray100 + .element2 + fixedSizeArray100[keyPath: identity(kp2)] + fixedSizeArray100.element1 += fixedSizeArray100 + .element0 - fixedSizeArray100[keyPath: identity(kp1)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element0 * fixedSizeArray100[keyPath: identity(kp7)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element2 / fixedSizeArray100[keyPath: identity(kp0)] + fixedSizeArray100.element6 += fixedSizeArray100 + .element5 + fixedSizeArray100[keyPath: identity(kp0)] + fixedSizeArray100.element3 += fixedSizeArray100 + .element8 - fixedSizeArray100[keyPath: identity(kp0)] + } + let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray100) + blackHole(sum) } From 221c0e5814c196e53ae2a158cd65b7d0db170ccc Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Mon, 15 Aug 2022 18:07:04 -0600 Subject: [PATCH 07/18] Added benchmarks to exhaustively benchmark all KeyPathComponent types. Removed benchmarks dealing with an inlining issue. --- .../KeyPathPerformanceTests.swift | 538 ++++++++++++------ 1 file changed, 371 insertions(+), 167 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index b1a93fd464031..61f764f8b65fa 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -37,22 +37,34 @@ public let benchmarks = [ setUpFunction: setupSingleton ), BenchmarkInfo( - name: "KeyPathInlined", - runFunction: run_testKeyPathsInlined, + name: "KeyPathsSmallStruct", + runFunction: run_testKeyPathSmallStruct, tags: [.validation, .api], setUpFunction: setupSingleton ), BenchmarkInfo( - name: "KeyPathsNotInlined", - runFunction: run_testKeyPathsNotInlined, + name: "KeyPathMutatingGetSet", + runFunction: run_KeyPathMutatingGetSet, tags: [.validation, .api], - setUpFunction: setupSingleton + setUpFunction: setupKeyPathNestedStructs ), BenchmarkInfo( - name: "KeyPathsSmallStruct", - runFunction: run_testKeyPathSmallStruct, + name: "KeyPathGet", + runFunction: run_KeyPathGet, tags: [.validation, .api], - setUpFunction: setupSingleton + setUpFunction: setupKeyPathNestedStructs + ), + BenchmarkInfo( + name: "KeyPathOptionals", + runFunction: run_KeyPathOptionals, + tags: [.validation, .api], + setUpFunction: setupKeyPathNestedStructs + ), + BenchmarkInfo( + name: "KeyPathNestedClasses", + runFunction: run_KeyPathNestedClasses, + tags: [.validation, .api], + setUpFunction: setupKeyPathNestedStructs ), ] @@ -68,12 +80,22 @@ class FixedSizeArrayHolder { var fixedSizeArray100: FixedSizeArray100 var fixedSizeArray10: FixedSizeArray10 var mainArrayForNestedStructs: [A] + var arrayForMutatingGetSet: [MutatingGetSetNested1] + var arrayForGet: [GetNested1] + var arrayForOptionals: [Optional1] + var arrayForNestedClasses: [C1] + var arrayForNonMutatingGetSet: [M] static let shared = FixedSizeArrayHolder() init() { fixedSizeArray100 = initializeFixedSizeArray100() fixedSizeArray10 = initializeFixedSizeArray10() mainArrayForNestedStructs = [A]() + arrayForMutatingGetSet = [MutatingGetSetNested1]() + arrayForGet = [GetNested1]() + arrayForOptionals = [Optional1]() + arrayForNestedClasses = [C1]() + arrayForNonMutatingGetSet = [M]() } } @@ -83,6 +105,16 @@ public func setupKeyPathNestedStructs() { for _ in 0 ..< numberOfElementsForNestedStructs { let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedStructs))))) holder.mainArrayForNestedStructs.append(instance) + + var mutatingGetSetInstance = MutatingGetSetNested1() + mutatingGetSetInstance.nestedItem.nestedItem.nestedItem.nestedItem + .mutating = expectedIntForNestedStructs + holder.arrayForMutatingGetSet.append(mutatingGetSetInstance) + holder.arrayForGet.append(GetNested1()) + holder.arrayForOptionals.append(Optional1()) + holder.arrayForNestedClasses.append(C1()) + holder.arrayForNonMutatingGetSet + .append(M(n: N(o: O(p: P(q: Q(q: expectedIntForNestedStructs)))))) } } @@ -143,6 +175,244 @@ struct E { var e: Int } +class C1 { + let a: Int = 0 + let r: C2 = .init() + class C2 { + let b: Int = 0 + let r: C3 = .init() + class C3 { + let a: Int = 0 + let r: C4 = .init() + class C4 { + let a: Int = 0 + let r: C5 = .init() + class C5 { + let a: Int = expectedIntForNestedStructs + } + } + } + } +} + +class M { + var n: N + init(n: N) { self.n = n } +} + +class N { + var o: O + init(o: O) { self.o = o } +} + +class O { + var p: P + init(p: P) { self.p = p } +} + +class P { + var q: Q + init(q: Q) { self.q = q } +} + +class Q { + var q: Int + init(q: Int) { self.q = q } +} + +// Used for run_KeyPathMutatingGetSet() +struct MutatingGetSetNested1 { + var _storage: Int + var _nestedItemStorage: MutatingGetSetNested2 + var mutating: Int { + get { return _storage } + set { _storage = newValue } + } + + var nestedItem: MutatingGetSetNested2 { + get { return _nestedItemStorage } + set { _nestedItemStorage = newValue } + } + + init() { + _storage = 0 + _nestedItemStorage = MutatingGetSetNested2() + } +} + +struct MutatingGetSetNested2 { + var _storage: Int + var _nestedItemStorage: MutatingGetSetNested3 + var mutating: Int { + get { return _storage } + set { _storage = newValue } + } + + var nestedItem: MutatingGetSetNested3 { + get { return _nestedItemStorage } + set { _nestedItemStorage = newValue } + } + + init() { + _storage = 0 + _nestedItemStorage = MutatingGetSetNested3() + } +} + +struct MutatingGetSetNested3 { + var _storage: Int + var _nestedItemStorage: MutatingGetSetNested4 + var mutating: Int { + get { return _storage } + set { _storage = newValue } + } + + var nestedItem: MutatingGetSetNested4 { + get { return _nestedItemStorage } + set { _nestedItemStorage = newValue } + } + + init() { + _storage = 0 + _nestedItemStorage = MutatingGetSetNested4() + } +} + +struct MutatingGetSetNested4 { + var _storage: Int + var _nestedItemStorage: MutatingGetSetNested5 + var mutating: Int { + get { return _storage } + set { _storage = newValue } + } + + var nestedItem: MutatingGetSetNested5 { + get { return _nestedItemStorage } + set { _nestedItemStorage = newValue } + } + + init() { + _storage = 0 + _nestedItemStorage = MutatingGetSetNested5() + } +} + +struct MutatingGetSetNested5 { + var _storage: Int + var mutating: Int { + get { return _storage } + set { _storage = newValue } + } + + init() { + _storage = 10 + } +} + +struct GetNested1 { + var _storage: Int + var _nestedItemStorage: GetNested2 + var mutating: Int { return _storage } + + var nestedItem: GetNested2 { return _nestedItemStorage } + + init() { + _storage = 0 + _nestedItemStorage = GetNested2() + } +} + +struct GetNested2 { + var _storage: Int + var _nestedItemStorage: GetNested3 + var mutating: Int { return _storage } + + var nestedItem: GetNested3 { return _nestedItemStorage } + + init() { + _storage = 0 + _nestedItemStorage = GetNested3() + } +} + +struct GetNested3 { + var _storage: Int + var _nestedItemStorage: GetNested4 + var mutating: Int { return _storage } + + var nestedItem: GetNested4 { return _nestedItemStorage } + + init() { + _storage = 0 + _nestedItemStorage = GetNested4() + } +} + +struct GetNested4 { + var _storage: Int + var _nestedItemStorage: GetNested5 + var mutating: Int { return _storage } + + var nestedItem: GetNested5 { return _nestedItemStorage } + + init() { + _storage = 0 + _nestedItemStorage = GetNested5() + } +} + +struct GetNested5 { + var _storage: Int + var mutating: Int { return _storage } + + init() { + _storage = expectedIntForNestedStructs + } +} + +struct Optional1 { + var _storage: Int + var _nestedItemStorage: Optional2?? + init() { + _storage = 0 + _nestedItemStorage = Optional2() + } +} + +struct Optional2 { + var _storage: Int + var _nestedItemStorage: Optional3?? + init() { + _storage = 0 + _nestedItemStorage = Optional3() + } +} + +struct Optional3 { + var _storage: Int + var _nestedItemStorage: Optional4?? + init() { + _storage = 0 + _nestedItemStorage = Optional4() + } +} + +struct Optional4 { + var _storage: Int + var _nestedItemStorage: Optional5?? + init() { + _storage = 0 + _nestedItemStorage = Optional5() + } +} + +struct Optional5 { + public var _storage: Int + init() { + _storage = expectedIntForNestedStructs + } +} + // Used for run_KeyPathFixedSizeArrayAccess() and run_testDirectAccess(). struct FixedSizeArray100: Sequence, IteratorProtocol { let count: Int = 100 @@ -1115,165 +1385,6 @@ public func run_testKeyPathWritePerformance(n: Int) { check(sum > ElementType(0)) } -/* - The two benchmarks below demonstrate an issue where, after a certain point, a collection of - nearby keypaths no longer get inlined by the compiler. Namely, if the memory addresses end - up being too far apart, the inlining process won't proceed. This divergence occurs during - one of the SIL optimization passes. The "@inline(never)" directive has been intentionally - omitted, as well as wrapping the KeyPaths in the identity() function in order to highlight - this issue. These two benchmarks will only differ in performance when compiled in release - mode, namely, "swift build --configuration release". Inlining will happen if "lastKeyPathIndex" - is set to any integer from 0 to 13, but will not happen if set to 14 to 49. - */ - -public func run_testKeyPathsInlined(n: Int) { - var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - let iterationMultipier = 1250 - - let lastKeyPathIndex = 9 - let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) - let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) - let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) - let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) - let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) - let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) - let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) - let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) - let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) - let kp9 = FixedSizeArray100.getKeypathToElement(index: lastKeyPathIndex) - - for t in 1 ... iterationMultipier * n { - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp1] + ElementType(t) - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp3] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp4] - fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp6] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp7] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp6] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp3] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp8] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp7] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp2] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp6] - fixedSizeArray100[keyPath: kp8] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp4] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp1] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp1] + - fixedSizeArray100[keyPath: kp5] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp3] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp1] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp0] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp5] - fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp8] + - fixedSizeArray100[keyPath: kp0] - } - let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) - - blackHole(sum) -} - -public func run_testKeyPathsNotInlined(n: Int) { - var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 - let iterationMultipier = 100 - - let lastKeyPathIndex = 44 - let kp0 = FixedSizeArray100.getKeypathToElement(index: 0) - let kp1 = FixedSizeArray100.getKeypathToElement(index: 1) - let kp2 = FixedSizeArray100.getKeypathToElement(index: 2) - let kp3 = FixedSizeArray100.getKeypathToElement(index: 3) - let kp4 = FixedSizeArray100.getKeypathToElement(index: 4) - let kp5 = FixedSizeArray100.getKeypathToElement(index: 5) - let kp6 = FixedSizeArray100.getKeypathToElement(index: 6) - let kp7 = FixedSizeArray100.getKeypathToElement(index: 7) - let kp8 = FixedSizeArray100.getKeypathToElement(index: 8) - let kp9 = FixedSizeArray100.getKeypathToElement(index: lastKeyPathIndex) - - for t in 1 ... iterationMultipier * n { - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp1] + ElementType(t) - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp3] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp4] - fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp6] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp7] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp6] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp3] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp8] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp7] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp9] - fixedSizeArray100[keyPath: kp2] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp6] - fixedSizeArray100[keyPath: kp8] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp9] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp4] - fixedSizeArray100[keyPath: kp4] += fixedSizeArray100[keyPath: kp1] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp5] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp9] += fixedSizeArray100[keyPath: kp1] + - fixedSizeArray100[keyPath: kp5] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp3] - fixedSizeArray100[keyPath: kp0] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp2] - fixedSizeArray100[keyPath: kp1] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp1] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp0] + - fixedSizeArray100[keyPath: kp7] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp2] + - fixedSizeArray100[keyPath: kp0] - fixedSizeArray100[keyPath: kp6] += fixedSizeArray100[keyPath: kp5] + - fixedSizeArray100[keyPath: kp5] - fixedSizeArray100[keyPath: kp3] += fixedSizeArray100[keyPath: kp8] + - fixedSizeArray100[keyPath: kp0] - } - let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) - blackHole(sum) -} - - // Intended to showcase the difference in performance between 10 and 100-element structs. @inline(never) public func run_testKeyPathSmallStruct(n: Int) { @@ -1347,3 +1458,96 @@ public func run_testKeyPathSmallStruct(n: Int) { let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray100) blackHole(sum) } + +// Benchmarks traversal through `KeyPathComponents` of type `.mutatingGetSet`. +public func run_KeyPathMutatingGetSet(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 200 + + let destinationKeyPath = \MutatingGetSetNested1.nestedItem.nestedItem.nestedItem.nestedItem + .mutating + + let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetSet.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.arrayForMutatingGetSet[index] + sum += element[keyPath: identity(destinationKeyPath)] + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedStructs) +} + +// Benchmarks traversal through `KeyPathComponents` of type `.get`. +public func run_KeyPathGet(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 200 + + let destinationKeyPath = \GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.mutating + let elementCount = FixedSizeArrayHolder.shared.arrayForGet.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.arrayForGet[index] + sum += element[keyPath: identity(destinationKeyPath)] + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedStructs) +} + +// Benchmarks traversal through `KeyPathComponents` of type `.optionalChain,` `.optionalForce,` and `.optionalWrap`. +// Note: The decision that `optionalChains` and `optionalForces` are merged into a single benchmark is arbitrary. +// TODO: Write a benchmark with more than one `.optionalWrap`. +public func run_KeyPathOptionals(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 200 + + let destinationKeyPath = \Optional1._nestedItemStorage?!._nestedItemStorage?!._nestedItemStorage?! + ._nestedItemStorage?!._storage + let elementCount = FixedSizeArrayHolder.shared.arrayForOptionals.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.arrayForOptionals[index] + sum += element[keyPath: identity(destinationKeyPath)]! + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedStructs) +} + +// Benchmarks traversal through `KeyPathComponents` of type `.class`. +public func run_KeyPathNestedClasses(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 200 + + let destinationKeyPath = \C1.r.r.r.r.a + let elementCount = FixedSizeArrayHolder.shared.arrayForNestedClasses.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.arrayForNestedClasses[index] + sum += element[keyPath: identity(destinationKeyPath)] + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedStructs) +} + +// Benchmarks traversal through `KeyPathComponents` of type `.nonmutatingGetSet`. +// Note: There may be other, simpler ways of getting a `.nonmutatingGetSet`. +public func run_KeyPathNonmutatingGetSet(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 200 + + let kp0: WritableKeyPath = \M.n + let kp1: WritableKeyPath = \N.o + let kp2: WritableKeyPath = \O.p + let kp3: WritableKeyPath = \P.q + let kp4: WritableKeyPath = \Q.q + let appendedKeyPath = kp0.appending(path: kp1).appending(path: kp2).appending(path: kp3) + .appending(path: kp4) + + let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet[index] + sum += element[keyPath: identity(appendedKeyPath)] + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedStructs) +} From 1b7d8347dbb595e3c2eab8cb8f135d0a0f19df65 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Tue, 16 Aug 2022 12:10:59 -0600 Subject: [PATCH 08/18] Wrapped additional keypaths with identity() where needed. More cleanup and documentation. --- .../KeyPathPerformanceTests.swift | 204 +++++++++--------- 1 file changed, 107 insertions(+), 97 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 61f764f8b65fa..ebb4524728018 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -68,8 +68,25 @@ public let benchmarks = [ ), ] +/** + There are several types of KeyPathComponents. + The following table indicates which type is featured in which benchmark. + Note that some types may be observed in other benchmarks as well. + + | KeyPathComponent Type |Featured in Benchmark | + |-----------------------|:---------------------------------| + | struct |*run_KeyPathNestedStruct()* | + | class |*run_KeyPathNestedClasses()* | + | get |*run_KeyPathGet()* | + | mutatingGetSet |*run_KeyPathMutatingGetSet()* | + | nonmutatingGetSet |*run_KeyPathNonmutatingGetSet()* | + | optionalChain |*run_KeyPathOptionals()* | + | optionalForce |*run_KeyPathOptionals()* | + | optionalWrap |*run_KeyPathOptionals()* | + */ + let numberOfElementsForNestedStructs = 20000 -let expectedIntForNestedStructs = 3 +let expectedIntForNestedItems = 3 // Make it easy to switch between primitives of different types (Int, Float, Double, etc.). typealias ElementType = Double @@ -103,18 +120,18 @@ class FixedSizeArrayHolder { public func setupKeyPathNestedStructs() { let holder = FixedSizeArrayHolder.shared for _ in 0 ..< numberOfElementsForNestedStructs { - let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedStructs))))) + let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedItems))))) holder.mainArrayForNestedStructs.append(instance) var mutatingGetSetInstance = MutatingGetSetNested1() mutatingGetSetInstance.nestedItem.nestedItem.nestedItem.nestedItem - .mutating = expectedIntForNestedStructs + .storage = expectedIntForNestedItems holder.arrayForMutatingGetSet.append(mutatingGetSetInstance) holder.arrayForGet.append(GetNested1()) holder.arrayForOptionals.append(Optional1()) holder.arrayForNestedClasses.append(C1()) holder.arrayForNonMutatingGetSet - .append(M(n: N(o: O(p: P(q: Q(q: expectedIntForNestedStructs)))))) + .append(M(n: N(o: O(p: P(q: Q(q: expectedIntForNestedItems)))))) } } @@ -122,6 +139,8 @@ public func setupSingleton() { blackHole(FixedSizeArrayHolder.shared) } +// We would like to compute the sums of the arrays once the iterations finish, +// and doing it this way it likely faster than using keypaths via getKeypathToElement(). func computeSumOfFixedSizeArray100(fixedSizeArray100: inout FixedSizeArray100) -> ElementType { @@ -175,6 +194,7 @@ struct E { var e: Int } +// Used for run_KeyPathNestedClasses() class C1 { let a: Int = 0 let r: C2 = .init() @@ -188,13 +208,14 @@ class C1 { let a: Int = 0 let r: C5 = .init() class C5 { - let a: Int = expectedIntForNestedStructs + let a: Int = expectedIntForNestedItems } } } } } +// Used for run_KeyPathNonmutatingGetSet() class M { var n: N init(n: N) { self.n = n } @@ -224,7 +245,7 @@ class Q { struct MutatingGetSetNested1 { var _storage: Int var _nestedItemStorage: MutatingGetSetNested2 - var mutating: Int { + var storage: Int { get { return _storage } set { _storage = newValue } } @@ -243,7 +264,7 @@ struct MutatingGetSetNested1 { struct MutatingGetSetNested2 { var _storage: Int var _nestedItemStorage: MutatingGetSetNested3 - var mutating: Int { + var storage: Int { get { return _storage } set { _storage = newValue } } @@ -262,7 +283,7 @@ struct MutatingGetSetNested2 { struct MutatingGetSetNested3 { var _storage: Int var _nestedItemStorage: MutatingGetSetNested4 - var mutating: Int { + var storage: Int { get { return _storage } set { _storage = newValue } } @@ -281,7 +302,7 @@ struct MutatingGetSetNested3 { struct MutatingGetSetNested4 { var _storage: Int var _nestedItemStorage: MutatingGetSetNested5 - var mutating: Int { + var storage: Int { get { return _storage } set { _storage = newValue } } @@ -299,7 +320,7 @@ struct MutatingGetSetNested4 { struct MutatingGetSetNested5 { var _storage: Int - var mutating: Int { + var storage: Int { get { return _storage } set { _storage = newValue } } @@ -309,13 +330,12 @@ struct MutatingGetSetNested5 { } } +// Used for run_KeyPathGet(). struct GetNested1 { var _storage: Int var _nestedItemStorage: GetNested2 - var mutating: Int { return _storage } - + var storage: Int { return _storage } var nestedItem: GetNested2 { return _nestedItemStorage } - init() { _storage = 0 _nestedItemStorage = GetNested2() @@ -325,10 +345,8 @@ struct GetNested1 { struct GetNested2 { var _storage: Int var _nestedItemStorage: GetNested3 - var mutating: Int { return _storage } - + var storage: Int { return _storage } var nestedItem: GetNested3 { return _nestedItemStorage } - init() { _storage = 0 _nestedItemStorage = GetNested3() @@ -338,10 +356,8 @@ struct GetNested2 { struct GetNested3 { var _storage: Int var _nestedItemStorage: GetNested4 - var mutating: Int { return _storage } - + var storage: Int { return _storage } var nestedItem: GetNested4 { return _nestedItemStorage } - init() { _storage = 0 _nestedItemStorage = GetNested4() @@ -351,10 +367,8 @@ struct GetNested3 { struct GetNested4 { var _storage: Int var _nestedItemStorage: GetNested5 - var mutating: Int { return _storage } - + var storage: Int { return _storage } var nestedItem: GetNested5 { return _nestedItemStorage } - init() { _storage = 0 _nestedItemStorage = GetNested5() @@ -363,13 +377,13 @@ struct GetNested4 { struct GetNested5 { var _storage: Int - var mutating: Int { return _storage } - + var storage: Int { return _storage } init() { - _storage = expectedIntForNestedStructs + _storage = expectedIntForNestedItems } } +// Used for run_KeyPathOptionals(). struct Optional1 { var _storage: Int var _nestedItemStorage: Optional2?? @@ -409,7 +423,7 @@ struct Optional4 { struct Optional5 { public var _storage: Int init() { - _storage = expectedIntForNestedStructs + _storage = expectedIntForNestedItems } } @@ -943,6 +957,7 @@ struct FixedSizeArray100: Sequence, IteratorProtocol { } } +// Used for run_testKeyPathSmallStruct(). struct FixedSizeArray10: Sequence, IteratorProtocol { let count: Int = 10 @@ -1144,6 +1159,7 @@ func initializeFixedSizeArray100() -> FixedSizeArray100 { ) } +// This measures the performance of keypath reads though nested structs. @inline(never) public func run_KeyPathNestedStructs(n: Int) { var sum = 0 @@ -1166,7 +1182,7 @@ public func run_KeyPathNestedStructs(n: Int) { sum += element[keyPath: identity(appendedKeyPath)] index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } // This is meant as a baseline, from a timing perspective, @@ -1208,6 +1224,7 @@ public func run_testDirectAccess(n: Int) { check(sum > ElementType(0)) } +// This measures read performance of keypaths. @inline(never) public func run_testKeyPathReadPerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 @@ -1388,7 +1405,7 @@ public func run_testKeyPathWritePerformance(n: Int) { // Intended to showcase the difference in performance between 10 and 100-element structs. @inline(never) public func run_testKeyPathSmallStruct(n: Int) { - var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray10 + var fixedSizeArray10 = FixedSizeArrayHolder.shared.fixedSizeArray10 let iterationMultipier = 25 let kp0 = FixedSizeArray10.getKeypathToElement(index: 0) @@ -1403,59 +1420,59 @@ public func run_testKeyPathSmallStruct(n: Int) { let kp9 = FixedSizeArray10.getKeypathToElement(index: 9) for t in 1 ... iterationMultipier * n { - fixedSizeArray100.element0 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100.element6 += fixedSizeArray100 - .element3 - fixedSizeArray100[keyPath: identity(kp9)] - fixedSizeArray100.element3 += fixedSizeArray100 - .element9 * fixedSizeArray100[keyPath: identity(kp7)] - fixedSizeArray100.element1 += fixedSizeArray100 - .element9 / fixedSizeArray100[keyPath: identity(kp9)] - fixedSizeArray100.element1 += fixedSizeArray100 - .element0 + fixedSizeArray100[keyPath: identity(kp4)] - fixedSizeArray100.element5 += fixedSizeArray100 - .element6 - fixedSizeArray100[keyPath: identity(kp2)] - fixedSizeArray100.element4 += fixedSizeArray100 - .element7 * fixedSizeArray100[keyPath: identity(kp2)] - fixedSizeArray100.element1 += fixedSizeArray100 - .element0 / fixedSizeArray100[keyPath: identity(kp6)] - fixedSizeArray100.element4 += fixedSizeArray100 - .element3 + fixedSizeArray100[keyPath: identity(kp9)] - fixedSizeArray100.element0 += fixedSizeArray100 - .element5 - fixedSizeArray100[keyPath: identity(kp8)] - fixedSizeArray100.element0 += fixedSizeArray100 - .element5 * fixedSizeArray100[keyPath: identity(kp7)] - fixedSizeArray100.element7 += fixedSizeArray100 - .element5 / fixedSizeArray100[keyPath: identity(kp9)] - fixedSizeArray100.element2 += fixedSizeArray100 - .element0 + fixedSizeArray100[keyPath: identity(kp6)] - fixedSizeArray100.element8 += fixedSizeArray100 - .element2 - fixedSizeArray100[keyPath: identity(kp2)] - fixedSizeArray100.element9 += fixedSizeArray100 - .element9 * fixedSizeArray100[keyPath: identity(kp2)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element0 / fixedSizeArray100[keyPath: identity(kp4)] - fixedSizeArray100.element4 += fixedSizeArray100 - .element1 + fixedSizeArray100[keyPath: identity(kp7)] - fixedSizeArray100.element5 += fixedSizeArray100 - .element2 - fixedSizeArray100[keyPath: identity(kp7)] - fixedSizeArray100.element9 += fixedSizeArray100 - .element1 * fixedSizeArray100[keyPath: identity(kp5)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element5 / fixedSizeArray100[keyPath: identity(kp3)] - fixedSizeArray100.element0 += fixedSizeArray100 - .element2 + fixedSizeArray100[keyPath: identity(kp2)] - fixedSizeArray100.element1 += fixedSizeArray100 - .element0 - fixedSizeArray100[keyPath: identity(kp1)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element0 * fixedSizeArray100[keyPath: identity(kp7)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element2 / fixedSizeArray100[keyPath: identity(kp0)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element5 + fixedSizeArray100[keyPath: identity(kp0)] - fixedSizeArray100.element3 += fixedSizeArray100 - .element8 - fixedSizeArray100[keyPath: identity(kp0)] + fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) + fixedSizeArray10.element6 += fixedSizeArray10 + .element3 - fixedSizeArray10[keyPath: identity(kp9)] + fixedSizeArray10.element3 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: identity(kp7)] + fixedSizeArray10.element1 += fixedSizeArray10 + .element9 / fixedSizeArray10[keyPath: identity(kp9)] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: identity(kp4)] + fixedSizeArray10.element5 += fixedSizeArray10 + .element6 - fixedSizeArray10[keyPath: identity(kp2)] + fixedSizeArray10.element4 += fixedSizeArray10 + .element7 * fixedSizeArray10[keyPath: identity(kp2)] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: identity(kp6)] + fixedSizeArray10.element4 += fixedSizeArray10 + .element3 + fixedSizeArray10[keyPath: identity(kp9)] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 - fixedSizeArray10[keyPath: identity(kp8)] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 * fixedSizeArray10[keyPath: identity(kp7)] + fixedSizeArray10.element7 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: identity(kp9)] + fixedSizeArray10.element2 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: identity(kp6)] + fixedSizeArray10.element8 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: identity(kp2)] + fixedSizeArray10.element9 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: identity(kp2)] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: identity(kp4)] + fixedSizeArray10.element4 += fixedSizeArray10 + .element1 + fixedSizeArray10[keyPath: identity(kp7)] + fixedSizeArray10.element5 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: identity(kp7)] + fixedSizeArray10.element9 += fixedSizeArray10 + .element1 * fixedSizeArray10[keyPath: identity(kp5)] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: identity(kp3)] + fixedSizeArray10.element0 += fixedSizeArray10 + .element2 + fixedSizeArray10[keyPath: identity(kp2)] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 - fixedSizeArray10[keyPath: identity(kp1)] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 * fixedSizeArray10[keyPath: identity(kp7)] + fixedSizeArray10.element6 += fixedSizeArray10 + .element2 / fixedSizeArray10[keyPath: identity(kp0)] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 + fixedSizeArray10[keyPath: identity(kp0)] + fixedSizeArray10.element3 += fixedSizeArray10 + .element8 - fixedSizeArray10[keyPath: identity(kp0)] } - let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray100) + let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray10) blackHole(sum) } @@ -1466,7 +1483,7 @@ public func run_KeyPathMutatingGetSet(n: Int) { let iterationMultipier = 200 let destinationKeyPath = \MutatingGetSetNested1.nestedItem.nestedItem.nestedItem.nestedItem - .mutating + .storage let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetSet.count for _ in 1 ... iterationMultipier * n { @@ -1474,7 +1491,7 @@ public func run_KeyPathMutatingGetSet(n: Int) { sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } // Benchmarks traversal through `KeyPathComponents` of type `.get`. @@ -1483,14 +1500,14 @@ public func run_KeyPathGet(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.mutating + let destinationKeyPath = \GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.storage let elementCount = FixedSizeArrayHolder.shared.arrayForGet.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForGet[index] sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } // Benchmarks traversal through `KeyPathComponents` of type `.optionalChain,` `.optionalForce,` and `.optionalWrap`. @@ -1509,7 +1526,7 @@ public func run_KeyPathOptionals(n: Int) { sum += element[keyPath: identity(destinationKeyPath)]! index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } // Benchmarks traversal through `KeyPathComponents` of type `.class`. @@ -1525,29 +1542,22 @@ public func run_KeyPathNestedClasses(n: Int) { sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } // Benchmarks traversal through `KeyPathComponents` of type `.nonmutatingGetSet`. -// Note: There may be other, simpler ways of getting a `.nonmutatingGetSet`. +// Note: There may be other, simpler ways of getting consecutive `.nonmutatingGetSet`. components. public func run_KeyPathNonmutatingGetSet(n: Int) { var sum = 0 var index = 0 let iterationMultipier = 200 - let kp0: WritableKeyPath = \M.n - let kp1: WritableKeyPath = \N.o - let kp2: WritableKeyPath = \O.p - let kp3: WritableKeyPath = \P.q - let kp4: WritableKeyPath = \Q.q - let appendedKeyPath = kp0.appending(path: kp1).appending(path: kp2).appending(path: kp3) - .appending(path: kp4) - + let destinationKeyPath = \M.n.o.p.q.q let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet[index] - sum += element[keyPath: identity(appendedKeyPath)] + sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } - check(sum == iterationMultipier * n * expectedIntForNestedStructs) + check(sum == iterationMultipier * n * expectedIntForNestedItems) } From 8562c6a60ccc5b07d9e3bd898cd059fed1269791 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Wed, 17 Aug 2022 10:44:04 -0600 Subject: [PATCH 09/18] Moved KeyPaths for KeyPathRead and Write into FixedSizeArrayHolder. Renamed GetSet to Getset. --- .../KeyPathPerformanceTests.swift | 285 ++++++++++++------ 1 file changed, 194 insertions(+), 91 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index ebb4524728018..5b4f1e802c938 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -43,8 +43,8 @@ public let benchmarks = [ setUpFunction: setupSingleton ), BenchmarkInfo( - name: "KeyPathMutatingGetSet", - runFunction: run_KeyPathMutatingGetSet, + name: "KeyPathMutatingGetset", + runFunction: run_KeyPathMutatingGetset, tags: [.validation, .api], setUpFunction: setupKeyPathNestedStructs ), @@ -78,8 +78,8 @@ public let benchmarks = [ | struct |*run_KeyPathNestedStruct()* | | class |*run_KeyPathNestedClasses()* | | get |*run_KeyPathGet()* | - | mutatingGetSet |*run_KeyPathMutatingGetSet()* | - | nonmutatingGetSet |*run_KeyPathNonmutatingGetSet()* | + | mutatingGetset |*run_KeyPathMutatingGetset()* | + | nonmutatingGetset |*run_KeyPathNonmutatingGetset()* | | optionalChain |*run_KeyPathOptionals()* | | optionalForce |*run_KeyPathOptionals()* | | optionalWrap |*run_KeyPathOptionals()* | @@ -97,22 +97,125 @@ class FixedSizeArrayHolder { var fixedSizeArray100: FixedSizeArray100 var fixedSizeArray10: FixedSizeArray10 var mainArrayForNestedStructs: [A] - var arrayForMutatingGetSet: [MutatingGetSetNested1] + var arrayForMutatingGetset: [MutatingGetsetNested1] var arrayForGet: [GetNested1] var arrayForOptionals: [Optional1] var arrayForNestedClasses: [C1] - var arrayForNonMutatingGetSet: [M] + var arrayForNonMutatingGetset: [M] + + // Same order as in KeyPathWritePerformance + var kp46: WritableKeyPath, ElementType> + var kp43: WritableKeyPath, ElementType> + var kp81: WritableKeyPath, ElementType> + var kp31: WritableKeyPath, ElementType> + var kp35: WritableKeyPath, ElementType> + var kp14: WritableKeyPath, ElementType> + var kp51: WritableKeyPath, ElementType> + var kp24: WritableKeyPath, ElementType> + var kp90: WritableKeyPath, ElementType> + var kp80: WritableKeyPath, ElementType> + var kp47: WritableKeyPath, ElementType> + var kp92: WritableKeyPath, ElementType> + var kp78: WritableKeyPath, ElementType> + var kp79: WritableKeyPath, ElementType> + var kp48: WritableKeyPath, ElementType> + var kp64: WritableKeyPath, ElementType> + var kp65: WritableKeyPath, ElementType> + var kp19: WritableKeyPath, ElementType> + var kp66: WritableKeyPath, ElementType> + var kp10: WritableKeyPath, ElementType> + var kp89: WritableKeyPath, ElementType> + var kp16: WritableKeyPath, ElementType> + var kp06: WritableKeyPath, ElementType> + var kp26: WritableKeyPath, ElementType> + var kp83: WritableKeyPath, ElementType> + + // Same order as in KeyPathReadPerformance, with duplicates removed. + var kp99: WritableKeyPath, ElementType> + var kp27: WritableKeyPath, ElementType> + var kp09: WritableKeyPath, ElementType> + var kp84: WritableKeyPath, ElementType> + var kp22: WritableKeyPath, ElementType> + var kp82: WritableKeyPath, ElementType> + var kp86: WritableKeyPath, ElementType> + var kp49: WritableKeyPath, ElementType> + var kp18: WritableKeyPath, ElementType> + var kp97: WritableKeyPath, ElementType> + var kp69: WritableKeyPath, ElementType> + var kp76: WritableKeyPath, ElementType> + var kp32: WritableKeyPath, ElementType> + var kp52: WritableKeyPath, ElementType> + var kp87: WritableKeyPath, ElementType> + var kp67: WritableKeyPath, ElementType> + var kp12: WritableKeyPath, ElementType> + var kp21: WritableKeyPath, ElementType> + var kp77: WritableKeyPath, ElementType> + var kp40: WritableKeyPath, ElementType> + var kp60: WritableKeyPath, ElementType> + var kp50: WritableKeyPath, ElementType> static let shared = FixedSizeArrayHolder() init() { fixedSizeArray100 = initializeFixedSizeArray100() fixedSizeArray10 = initializeFixedSizeArray10() mainArrayForNestedStructs = [A]() - arrayForMutatingGetSet = [MutatingGetSetNested1]() + arrayForMutatingGetset = [MutatingGetsetNested1]() arrayForGet = [GetNested1]() arrayForOptionals = [Optional1]() arrayForNestedClasses = [C1]() - arrayForNonMutatingGetSet = [M]() + arrayForNonMutatingGetset = [M]() + + kp46 = FixedSizeArray100.getKeypathToElement(index: 46) + kp43 = FixedSizeArray100.getKeypathToElement(index: 43) + kp81 = FixedSizeArray100.getKeypathToElement(index: 81) + kp31 = FixedSizeArray100.getKeypathToElement(index: 31) + kp35 = FixedSizeArray100.getKeypathToElement(index: 35) + kp14 = FixedSizeArray100.getKeypathToElement(index: 14) + kp51 = FixedSizeArray100.getKeypathToElement(index: 51) + kp24 = FixedSizeArray100.getKeypathToElement(index: 24) + kp90 = FixedSizeArray100.getKeypathToElement(index: 90) + kp80 = FixedSizeArray100.getKeypathToElement(index: 80) + kp47 = FixedSizeArray100.getKeypathToElement(index: 47) + kp92 = FixedSizeArray100.getKeypathToElement(index: 92) + kp78 = FixedSizeArray100.getKeypathToElement(index: 78) + kp79 = FixedSizeArray100.getKeypathToElement(index: 79) + kp48 = FixedSizeArray100.getKeypathToElement(index: 48) + kp64 = FixedSizeArray100.getKeypathToElement(index: 64) + kp65 = FixedSizeArray100.getKeypathToElement(index: 65) + kp19 = FixedSizeArray100.getKeypathToElement(index: 19) + kp66 = FixedSizeArray100.getKeypathToElement(index: 66) + kp10 = FixedSizeArray100.getKeypathToElement(index: 10) + kp89 = FixedSizeArray100.getKeypathToElement(index: 89) + kp16 = FixedSizeArray100.getKeypathToElement(index: 16) + kp06 = FixedSizeArray100.getKeypathToElement(index: 06) + kp26 = FixedSizeArray100.getKeypathToElement(index: 26) + kp83 = FixedSizeArray100.getKeypathToElement(index: 83) + + kp99 = FixedSizeArray100.getKeypathToElement(index: 99) + kp27 = FixedSizeArray100.getKeypathToElement(index: 27) + kp09 = FixedSizeArray100.getKeypathToElement(index: 09) + kp84 = FixedSizeArray100.getKeypathToElement(index: 84) + kp22 = FixedSizeArray100.getKeypathToElement(index: 22) + kp82 = FixedSizeArray100.getKeypathToElement(index: 82) + kp86 = FixedSizeArray100.getKeypathToElement(index: 86) + kp49 = FixedSizeArray100.getKeypathToElement(index: 49) + kp18 = FixedSizeArray100.getKeypathToElement(index: 18) + kp97 = FixedSizeArray100.getKeypathToElement(index: 97) + kp69 = FixedSizeArray100.getKeypathToElement(index: 69) + kp76 = FixedSizeArray100.getKeypathToElement(index: 76) + kp32 = FixedSizeArray100.getKeypathToElement(index: 32) + kp52 = FixedSizeArray100.getKeypathToElement(index: 52) + kp24 = FixedSizeArray100.getKeypathToElement(index: 24) + kp87 = FixedSizeArray100.getKeypathToElement(index: 87) + kp67 = FixedSizeArray100.getKeypathToElement(index: 67) + kp65 = FixedSizeArray100.getKeypathToElement(index: 65) + kp43 = FixedSizeArray100.getKeypathToElement(index: 43) + kp12 = FixedSizeArray100.getKeypathToElement(index: 12) + kp21 = FixedSizeArray100.getKeypathToElement(index: 21) + kp77 = FixedSizeArray100.getKeypathToElement(index: 77) + kp40 = FixedSizeArray100.getKeypathToElement(index: 40) + kp60 = FixedSizeArray100.getKeypathToElement(index: 60) + kp50 = FixedSizeArray100.getKeypathToElement(index: 50) } } @@ -123,14 +226,14 @@ public func setupKeyPathNestedStructs() { let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedItems))))) holder.mainArrayForNestedStructs.append(instance) - var mutatingGetSetInstance = MutatingGetSetNested1() - mutatingGetSetInstance.nestedItem.nestedItem.nestedItem.nestedItem + var mutatingGetsetInstance = MutatingGetsetNested1() + mutatingGetsetInstance.nestedItem.nestedItem.nestedItem.nestedItem .storage = expectedIntForNestedItems - holder.arrayForMutatingGetSet.append(mutatingGetSetInstance) + holder.arrayForMutatingGetset.append(mutatingGetsetInstance) holder.arrayForGet.append(GetNested1()) holder.arrayForOptionals.append(Optional1()) holder.arrayForNestedClasses.append(C1()) - holder.arrayForNonMutatingGetSet + holder.arrayForNonMutatingGetset .append(M(n: N(o: O(p: P(q: Q(q: expectedIntForNestedItems)))))) } } @@ -215,7 +318,7 @@ class C1 { } } -// Used for run_KeyPathNonmutatingGetSet() +// Used for run_KeyPathNonmutatingGetset() class M { var n: N init(n: N) { self.n = n } @@ -241,84 +344,84 @@ class Q { init(q: Int) { self.q = q } } -// Used for run_KeyPathMutatingGetSet() -struct MutatingGetSetNested1 { +// Used for run_KeyPathMutatingGetset() +struct MutatingGetsetNested1 { var _storage: Int - var _nestedItemStorage: MutatingGetSetNested2 + var _nestedItemStorage: MutatingGetsetNested2 var storage: Int { get { return _storage } set { _storage = newValue } } - var nestedItem: MutatingGetSetNested2 { + var nestedItem: MutatingGetsetNested2 { get { return _nestedItemStorage } set { _nestedItemStorage = newValue } } init() { _storage = 0 - _nestedItemStorage = MutatingGetSetNested2() + _nestedItemStorage = MutatingGetsetNested2() } } -struct MutatingGetSetNested2 { +struct MutatingGetsetNested2 { var _storage: Int - var _nestedItemStorage: MutatingGetSetNested3 + var _nestedItemStorage: MutatingGetsetNested3 var storage: Int { get { return _storage } set { _storage = newValue } } - var nestedItem: MutatingGetSetNested3 { + var nestedItem: MutatingGetsetNested3 { get { return _nestedItemStorage } set { _nestedItemStorage = newValue } } init() { _storage = 0 - _nestedItemStorage = MutatingGetSetNested3() + _nestedItemStorage = MutatingGetsetNested3() } } -struct MutatingGetSetNested3 { +struct MutatingGetsetNested3 { var _storage: Int - var _nestedItemStorage: MutatingGetSetNested4 + var _nestedItemStorage: MutatingGetsetNested4 var storage: Int { get { return _storage } set { _storage = newValue } } - var nestedItem: MutatingGetSetNested4 { + var nestedItem: MutatingGetsetNested4 { get { return _nestedItemStorage } set { _nestedItemStorage = newValue } } init() { _storage = 0 - _nestedItemStorage = MutatingGetSetNested4() + _nestedItemStorage = MutatingGetsetNested4() } } -struct MutatingGetSetNested4 { +struct MutatingGetsetNested4 { var _storage: Int - var _nestedItemStorage: MutatingGetSetNested5 + var _nestedItemStorage: MutatingGetsetNested5 var storage: Int { get { return _storage } set { _storage = newValue } } - var nestedItem: MutatingGetSetNested5 { + var nestedItem: MutatingGetsetNested5 { get { return _nestedItemStorage } set { _nestedItemStorage = newValue } } init() { _storage = 0 - _nestedItemStorage = MutatingGetSetNested5() + _nestedItemStorage = MutatingGetsetNested5() } } -struct MutatingGetSetNested5 { +struct MutatingGetsetNested5 { var _storage: Int var storage: Int { get { return _storage } @@ -1230,31 +1333,31 @@ public func run_testKeyPathReadPerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 25 - let kp99 = FixedSizeArray100.getKeypathToElement(index: 99) - let kp27 = FixedSizeArray100.getKeypathToElement(index: 27) - let kp09 = FixedSizeArray100.getKeypathToElement(index: 09) - let kp84 = FixedSizeArray100.getKeypathToElement(index: 84) - let kp22 = FixedSizeArray100.getKeypathToElement(index: 22) - let kp82 = FixedSizeArray100.getKeypathToElement(index: 82) - let kp86 = FixedSizeArray100.getKeypathToElement(index: 86) - let kp49 = FixedSizeArray100.getKeypathToElement(index: 49) - let kp18 = FixedSizeArray100.getKeypathToElement(index: 18) - let kp97 = FixedSizeArray100.getKeypathToElement(index: 97) - let kp69 = FixedSizeArray100.getKeypathToElement(index: 69) - let kp76 = FixedSizeArray100.getKeypathToElement(index: 76) - let kp32 = FixedSizeArray100.getKeypathToElement(index: 32) - let kp52 = FixedSizeArray100.getKeypathToElement(index: 52) - let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) - let kp87 = FixedSizeArray100.getKeypathToElement(index: 87) - let kp67 = FixedSizeArray100.getKeypathToElement(index: 67) - let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) - let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) - let kp12 = FixedSizeArray100.getKeypathToElement(index: 12) - let kp21 = FixedSizeArray100.getKeypathToElement(index: 21) - let kp77 = FixedSizeArray100.getKeypathToElement(index: 77) - let kp40 = FixedSizeArray100.getKeypathToElement(index: 40) - let kp60 = FixedSizeArray100.getKeypathToElement(index: 60) - let kp50 = FixedSizeArray100.getKeypathToElement(index: 50) + let kp99 = FixedSizeArrayHolder.shared.kp99 + let kp27 = FixedSizeArrayHolder.shared.kp27 + let kp09 = FixedSizeArrayHolder.shared.kp09 + let kp84 = FixedSizeArrayHolder.shared.kp84 + let kp22 = FixedSizeArrayHolder.shared.kp22 + let kp82 = FixedSizeArrayHolder.shared.kp82 + let kp86 = FixedSizeArrayHolder.shared.kp86 + let kp49 = FixedSizeArrayHolder.shared.kp49 + let kp18 = FixedSizeArrayHolder.shared.kp18 + let kp97 = FixedSizeArrayHolder.shared.kp97 + let kp69 = FixedSizeArrayHolder.shared.kp69 + let kp76 = FixedSizeArrayHolder.shared.kp76 + let kp32 = FixedSizeArrayHolder.shared.kp32 + let kp52 = FixedSizeArrayHolder.shared.kp52 + let kp24 = FixedSizeArrayHolder.shared.kp24 + let kp87 = FixedSizeArrayHolder.shared.kp87 + let kp67 = FixedSizeArrayHolder.shared.kp67 + let kp65 = FixedSizeArrayHolder.shared.kp65 + let kp43 = FixedSizeArrayHolder.shared.kp43 + let kp12 = FixedSizeArrayHolder.shared.kp12 + let kp21 = FixedSizeArrayHolder.shared.kp21 + let kp77 = FixedSizeArrayHolder.shared.kp77 + let kp40 = FixedSizeArrayHolder.shared.kp40 + let kp60 = FixedSizeArrayHolder.shared.kp60 + let kp50 = FixedSizeArrayHolder.shared.kp50 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) @@ -1319,31 +1422,31 @@ public func run_testKeyPathWritePerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 150 - let kp46 = FixedSizeArray100.getKeypathToElement(index: 46) - let kp43 = FixedSizeArray100.getKeypathToElement(index: 43) - let kp81 = FixedSizeArray100.getKeypathToElement(index: 81) - let kp31 = FixedSizeArray100.getKeypathToElement(index: 31) - let kp35 = FixedSizeArray100.getKeypathToElement(index: 35) - let kp14 = FixedSizeArray100.getKeypathToElement(index: 14) - let kp51 = FixedSizeArray100.getKeypathToElement(index: 51) - let kp24 = FixedSizeArray100.getKeypathToElement(index: 24) - let kp90 = FixedSizeArray100.getKeypathToElement(index: 90) - let kp80 = FixedSizeArray100.getKeypathToElement(index: 80) - let kp47 = FixedSizeArray100.getKeypathToElement(index: 47) - let kp92 = FixedSizeArray100.getKeypathToElement(index: 92) - let kp78 = FixedSizeArray100.getKeypathToElement(index: 78) - let kp79 = FixedSizeArray100.getKeypathToElement(index: 79) - let kp48 = FixedSizeArray100.getKeypathToElement(index: 48) - let kp64 = FixedSizeArray100.getKeypathToElement(index: 64) - let kp65 = FixedSizeArray100.getKeypathToElement(index: 65) - let kp19 = FixedSizeArray100.getKeypathToElement(index: 19) - let kp66 = FixedSizeArray100.getKeypathToElement(index: 66) - let kp10 = FixedSizeArray100.getKeypathToElement(index: 10) - let kp89 = FixedSizeArray100.getKeypathToElement(index: 89) - let kp16 = FixedSizeArray100.getKeypathToElement(index: 16) - let kp06 = FixedSizeArray100.getKeypathToElement(index: 06) - let kp26 = FixedSizeArray100.getKeypathToElement(index: 26) - let kp83 = FixedSizeArray100.getKeypathToElement(index: 83) + let kp46 = FixedSizeArrayHolder.shared.kp46 + let kp43 = FixedSizeArrayHolder.shared.kp43 + let kp81 = FixedSizeArrayHolder.shared.kp81 + let kp31 = FixedSizeArrayHolder.shared.kp31 + let kp35 = FixedSizeArrayHolder.shared.kp35 + let kp14 = FixedSizeArrayHolder.shared.kp14 + let kp51 = FixedSizeArrayHolder.shared.kp51 + let kp24 = FixedSizeArrayHolder.shared.kp24 + let kp90 = FixedSizeArrayHolder.shared.kp90 + let kp80 = FixedSizeArrayHolder.shared.kp80 + let kp47 = FixedSizeArrayHolder.shared.kp47 + let kp92 = FixedSizeArrayHolder.shared.kp92 + let kp78 = FixedSizeArrayHolder.shared.kp78 + let kp79 = FixedSizeArrayHolder.shared.kp79 + let kp48 = FixedSizeArrayHolder.shared.kp48 + let kp64 = FixedSizeArrayHolder.shared.kp64 + let kp65 = FixedSizeArrayHolder.shared.kp65 + let kp19 = FixedSizeArrayHolder.shared.kp19 + let kp66 = FixedSizeArrayHolder.shared.kp66 + let kp10 = FixedSizeArrayHolder.shared.kp10 + let kp89 = FixedSizeArrayHolder.shared.kp89 + let kp16 = FixedSizeArrayHolder.shared.kp16 + let kp06 = FixedSizeArrayHolder.shared.kp06 + let kp26 = FixedSizeArrayHolder.shared.kp26 + let kp83 = FixedSizeArrayHolder.shared.kp83 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) @@ -1476,18 +1579,18 @@ public func run_testKeyPathSmallStruct(n: Int) { blackHole(sum) } -// Benchmarks traversal through `KeyPathComponents` of type `.mutatingGetSet`. -public func run_KeyPathMutatingGetSet(n: Int) { +// Benchmarks traversal through `KeyPathComponents` of type `.mutatingGetset`. +public func run_KeyPathMutatingGetset(n: Int) { var sum = 0 var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \MutatingGetSetNested1.nestedItem.nestedItem.nestedItem.nestedItem + let destinationKeyPath = \MutatingGetsetNested1.nestedItem.nestedItem.nestedItem.nestedItem .storage - let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetSet.count + let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetset.count for _ in 1 ... iterationMultipier * n { - let element = FixedSizeArrayHolder.shared.arrayForMutatingGetSet[index] + let element = FixedSizeArrayHolder.shared.arrayForMutatingGetset[index] sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } @@ -1545,17 +1648,17 @@ public func run_KeyPathNestedClasses(n: Int) { check(sum == iterationMultipier * n * expectedIntForNestedItems) } -// Benchmarks traversal through `KeyPathComponents` of type `.nonmutatingGetSet`. -// Note: There may be other, simpler ways of getting consecutive `.nonmutatingGetSet`. components. -public func run_KeyPathNonmutatingGetSet(n: Int) { +// Benchmarks traversal through `KeyPathComponents` of type `.nonmutatingGetset`. +// Note: There may be other, simpler ways of getting consecutive `.nonmutatingGetset`. components. +public func run_KeyPathNonmutatingGetset(n: Int) { var sum = 0 var index = 0 let iterationMultipier = 200 let destinationKeyPath = \M.n.o.p.q.q - let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet.count + let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset.count for _ in 1 ... iterationMultipier * n { - let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetSet[index] + let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset[index] sum += element[keyPath: identity(destinationKeyPath)] index = (index + 1) % elementCount } From 0db40a08cdb8d06ff5a44d6082a3e4a6a47b74fc Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 19 Aug 2022 11:43:25 -0600 Subject: [PATCH 10/18] Added inline(never) to both versions of getKeypathToElement(). --- benchmark/single-source/KeyPathPerformanceTests.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 5b4f1e802c938..8650c0a92962b 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -839,6 +839,7 @@ struct FixedSizeArray100: Sequence, IteratorProtocol { var element98: Element var element99: Element + @inline(never) static func getKeypathToElement(index: Int) -> WritableKeyPath, Element> { @@ -1100,6 +1101,7 @@ struct FixedSizeArray10: Sequence, IteratorProtocol { var element8: Element var element9: Element + @inline(never) static func getKeypathToElement(index: Int) -> WritableKeyPath, Element> { From 1c9ba169fe229a614d4b852b80b4151e2ee794ca Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Mon, 22 Aug 2022 10:54:51 -0600 Subject: [PATCH 11/18] Moved identity() wraps so that they're called once per variable per benchmark. --- .../KeyPathPerformanceTests.swift | 456 +++++++++--------- 1 file changed, 228 insertions(+), 228 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 8650c0a92962b..ba71f6be466f1 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -1277,14 +1277,14 @@ public func run_KeyPathNestedStructs(n: Int) { let singleHopKeyPath3: WritableKeyPath = \D.e let singleHopKeyPath4: WritableKeyPath = \E.e - let appendedKeyPath = singleHopKeyPath0.appending(path: singleHopKeyPath1) + let appendedKeyPath = identity(singleHopKeyPath0.appending(path: singleHopKeyPath1) .appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3) - .appending(path: singleHopKeyPath4) + .appending(path: singleHopKeyPath4)) let elementCount = FixedSizeArrayHolder.shared.mainArrayForNestedStructs.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.mainArrayForNestedStructs[index] - sum += element[keyPath: identity(appendedKeyPath)] + sum += element[keyPath: appendedKeyPath] index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) @@ -1335,84 +1335,84 @@ public func run_testKeyPathReadPerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 25 - let kp99 = FixedSizeArrayHolder.shared.kp99 - let kp27 = FixedSizeArrayHolder.shared.kp27 - let kp09 = FixedSizeArrayHolder.shared.kp09 - let kp84 = FixedSizeArrayHolder.shared.kp84 - let kp22 = FixedSizeArrayHolder.shared.kp22 - let kp82 = FixedSizeArrayHolder.shared.kp82 - let kp86 = FixedSizeArrayHolder.shared.kp86 - let kp49 = FixedSizeArrayHolder.shared.kp49 - let kp18 = FixedSizeArrayHolder.shared.kp18 - let kp97 = FixedSizeArrayHolder.shared.kp97 - let kp69 = FixedSizeArrayHolder.shared.kp69 - let kp76 = FixedSizeArrayHolder.shared.kp76 - let kp32 = FixedSizeArrayHolder.shared.kp32 - let kp52 = FixedSizeArrayHolder.shared.kp52 - let kp24 = FixedSizeArrayHolder.shared.kp24 - let kp87 = FixedSizeArrayHolder.shared.kp87 - let kp67 = FixedSizeArrayHolder.shared.kp67 - let kp65 = FixedSizeArrayHolder.shared.kp65 - let kp43 = FixedSizeArrayHolder.shared.kp43 - let kp12 = FixedSizeArrayHolder.shared.kp12 - let kp21 = FixedSizeArrayHolder.shared.kp21 - let kp77 = FixedSizeArrayHolder.shared.kp77 - let kp40 = FixedSizeArrayHolder.shared.kp40 - let kp60 = FixedSizeArrayHolder.shared.kp60 - let kp50 = FixedSizeArrayHolder.shared.kp50 + let kp99 = identity(FixedSizeArrayHolder.shared.kp99) + let kp27 = identity(FixedSizeArrayHolder.shared.kp27) + let kp09 = identity(FixedSizeArrayHolder.shared.kp09) + let kp84 = identity(FixedSizeArrayHolder.shared.kp84) + let kp22 = identity(FixedSizeArrayHolder.shared.kp22) + let kp82 = identity(FixedSizeArrayHolder.shared.kp82) + let kp86 = identity(FixedSizeArrayHolder.shared.kp86) + let kp49 = identity(FixedSizeArrayHolder.shared.kp49) + let kp18 = identity(FixedSizeArrayHolder.shared.kp18) + let kp97 = identity(FixedSizeArrayHolder.shared.kp97) + let kp69 = identity(FixedSizeArrayHolder.shared.kp69) + let kp76 = identity(FixedSizeArrayHolder.shared.kp76) + let kp32 = identity(FixedSizeArrayHolder.shared.kp32) + let kp52 = identity(FixedSizeArrayHolder.shared.kp52) + let kp24 = identity(FixedSizeArrayHolder.shared.kp24) + let kp87 = identity(FixedSizeArrayHolder.shared.kp87) + let kp67 = identity(FixedSizeArrayHolder.shared.kp67) + let kp65 = identity(FixedSizeArrayHolder.shared.kp65) + let kp43 = identity(FixedSizeArrayHolder.shared.kp43) + let kp12 = identity(FixedSizeArrayHolder.shared.kp12) + let kp21 = identity(FixedSizeArrayHolder.shared.kp21) + let kp77 = identity(FixedSizeArrayHolder.shared.kp77) + let kp40 = identity(FixedSizeArrayHolder.shared.kp40) + let kp60 = identity(FixedSizeArrayHolder.shared.kp60) + let kp50 = identity(FixedSizeArrayHolder.shared.kp50) for t in 1 ... iterationMultipier * n { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100.element46 += fixedSizeArray100 - .element63 - fixedSizeArray100[keyPath: identity(kp99)] - fixedSizeArray100.element43 += fixedSizeArray100 - .element39 * fixedSizeArray100[keyPath: identity(kp27)] - fixedSizeArray100.element81 += fixedSizeArray100 - .element49 / fixedSizeArray100[keyPath: identity(kp09)] - fixedSizeArray100.element31 += fixedSizeArray100 - .element90 + fixedSizeArray100[keyPath: identity(kp84)] - fixedSizeArray100.element35 += fixedSizeArray100 - .element6 - fixedSizeArray100[keyPath: identity(kp22)] - fixedSizeArray100.element14 += fixedSizeArray100 - .element67 * fixedSizeArray100[keyPath: identity(kp82)] - fixedSizeArray100.element51 += fixedSizeArray100 - .element40 / fixedSizeArray100[keyPath: identity(kp86)] - fixedSizeArray100.element24 += fixedSizeArray100 - .element23 + fixedSizeArray100[keyPath: identity(kp49)] - fixedSizeArray100.element90 += fixedSizeArray100 - .element95 - fixedSizeArray100[keyPath: identity(kp18)] - fixedSizeArray100.element80 += fixedSizeArray100 - .element45 * fixedSizeArray100[keyPath: identity(kp97)] - fixedSizeArray100.element47 += fixedSizeArray100 - .element65 / fixedSizeArray100[keyPath: identity(kp69)] - fixedSizeArray100.element92 += fixedSizeArray100 - .element80 + fixedSizeArray100[keyPath: identity(kp76)] - fixedSizeArray100.element78 += fixedSizeArray100 - .element32 - fixedSizeArray100[keyPath: identity(kp32)] - fixedSizeArray100.element79 += fixedSizeArray100 - .element59 * fixedSizeArray100[keyPath: identity(kp52)] - fixedSizeArray100.element46 += fixedSizeArray100 - .element60 / fixedSizeArray100[keyPath: identity(kp24)] - fixedSizeArray100.element64 += fixedSizeArray100 - .element41 + fixedSizeArray100[keyPath: identity(kp87)] - fixedSizeArray100.element65 += fixedSizeArray100 - .element72 - fixedSizeArray100[keyPath: identity(kp67)] - fixedSizeArray100.element19 += fixedSizeArray100 - .element81 * fixedSizeArray100[keyPath: identity(kp65)] - fixedSizeArray100.element66 += fixedSizeArray100 - .element55 / fixedSizeArray100[keyPath: identity(kp43)] - fixedSizeArray100.element10 += fixedSizeArray100 - .element52 + fixedSizeArray100[keyPath: identity(kp12)] - fixedSizeArray100.element81 += fixedSizeArray100 - .element50 - fixedSizeArray100[keyPath: identity(kp21)] - fixedSizeArray100.element16 += fixedSizeArray100 - .element80 * fixedSizeArray100[keyPath: identity(kp77)] - fixedSizeArray100.element6 += fixedSizeArray100 - .element62 / fixedSizeArray100[keyPath: identity(kp40)] - fixedSizeArray100.element26 += fixedSizeArray100 - .element65 + fixedSizeArray100[keyPath: identity(kp60)] - fixedSizeArray100.element83 += fixedSizeArray100 - .element78 - fixedSizeArray100[keyPath: identity(kp50)] + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) + fixedSizeArray100.element46 += fixedSizeArray100 + .element63 - fixedSizeArray100[keyPath: kp99] + fixedSizeArray100.element43 += fixedSizeArray100 + .element39 * fixedSizeArray100[keyPath: kp27] + fixedSizeArray100.element81 += fixedSizeArray100 + .element49 / fixedSizeArray100[keyPath: kp09] + fixedSizeArray100.element31 += fixedSizeArray100 + .element90 + fixedSizeArray100[keyPath: kp84] + fixedSizeArray100.element35 += fixedSizeArray100 + .element6 - fixedSizeArray100[keyPath: kp22] + fixedSizeArray100.element14 += fixedSizeArray100 + .element67 * fixedSizeArray100[keyPath: kp82] + fixedSizeArray100.element51 += fixedSizeArray100 + .element40 / fixedSizeArray100[keyPath: kp86] + fixedSizeArray100.element24 += fixedSizeArray100 + .element23 + fixedSizeArray100[keyPath: kp49] + fixedSizeArray100.element90 += fixedSizeArray100 + .element95 - fixedSizeArray100[keyPath: kp18] + fixedSizeArray100.element80 += fixedSizeArray100 + .element45 * fixedSizeArray100[keyPath: kp97] + fixedSizeArray100.element47 += fixedSizeArray100 + .element65 / fixedSizeArray100[keyPath: kp69] + fixedSizeArray100.element92 += fixedSizeArray100 + .element80 + fixedSizeArray100[keyPath: kp76] + fixedSizeArray100.element78 += fixedSizeArray100 + .element32 - fixedSizeArray100[keyPath: kp32] + fixedSizeArray100.element79 += fixedSizeArray100 + .element59 * fixedSizeArray100[keyPath: kp52] + fixedSizeArray100.element46 += fixedSizeArray100 + .element60 / fixedSizeArray100[keyPath: kp24] + fixedSizeArray100.element64 += fixedSizeArray100 + .element41 + fixedSizeArray100[keyPath: kp87] + fixedSizeArray100.element65 += fixedSizeArray100 + .element72 - fixedSizeArray100[keyPath: kp67] + fixedSizeArray100.element19 += fixedSizeArray100 + .element81 * fixedSizeArray100[keyPath: kp65] + fixedSizeArray100.element66 += fixedSizeArray100 + .element55 / fixedSizeArray100[keyPath: kp43] + fixedSizeArray100.element10 += fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: kp12] + fixedSizeArray100.element81 += fixedSizeArray100 + .element50 - fixedSizeArray100[keyPath: kp21] + fixedSizeArray100.element16 += fixedSizeArray100 + .element80 * fixedSizeArray100[keyPath: kp77] + fixedSizeArray100.element6 += fixedSizeArray100 + .element62 / fixedSizeArray100[keyPath: kp40] + fixedSizeArray100.element26 += fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp60] + fixedSizeArray100.element83 += fixedSizeArray100 + .element78 - fixedSizeArray100[keyPath: kp50] } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) check(sum > ElementType(0)) @@ -1424,84 +1424,84 @@ public func run_testKeyPathWritePerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 150 - let kp46 = FixedSizeArrayHolder.shared.kp46 - let kp43 = FixedSizeArrayHolder.shared.kp43 - let kp81 = FixedSizeArrayHolder.shared.kp81 - let kp31 = FixedSizeArrayHolder.shared.kp31 - let kp35 = FixedSizeArrayHolder.shared.kp35 - let kp14 = FixedSizeArrayHolder.shared.kp14 - let kp51 = FixedSizeArrayHolder.shared.kp51 - let kp24 = FixedSizeArrayHolder.shared.kp24 - let kp90 = FixedSizeArrayHolder.shared.kp90 - let kp80 = FixedSizeArrayHolder.shared.kp80 - let kp47 = FixedSizeArrayHolder.shared.kp47 - let kp92 = FixedSizeArrayHolder.shared.kp92 - let kp78 = FixedSizeArrayHolder.shared.kp78 - let kp79 = FixedSizeArrayHolder.shared.kp79 - let kp48 = FixedSizeArrayHolder.shared.kp48 - let kp64 = FixedSizeArrayHolder.shared.kp64 - let kp65 = FixedSizeArrayHolder.shared.kp65 - let kp19 = FixedSizeArrayHolder.shared.kp19 - let kp66 = FixedSizeArrayHolder.shared.kp66 - let kp10 = FixedSizeArrayHolder.shared.kp10 - let kp89 = FixedSizeArrayHolder.shared.kp89 - let kp16 = FixedSizeArrayHolder.shared.kp16 - let kp06 = FixedSizeArrayHolder.shared.kp06 - let kp26 = FixedSizeArrayHolder.shared.kp26 - let kp83 = FixedSizeArrayHolder.shared.kp83 + let kp46 = identity(FixedSizeArrayHolder.shared.kp46) + let kp43 = identity(FixedSizeArrayHolder.shared.kp43) + let kp81 = identity(FixedSizeArrayHolder.shared.kp81) + let kp31 = identity(FixedSizeArrayHolder.shared.kp31) + let kp35 = identity(FixedSizeArrayHolder.shared.kp35) + let kp14 = identity(FixedSizeArrayHolder.shared.kp14) + let kp51 = identity(FixedSizeArrayHolder.shared.kp51) + let kp24 = identity(FixedSizeArrayHolder.shared.kp24) + let kp90 = identity(FixedSizeArrayHolder.shared.kp90) + let kp80 = identity(FixedSizeArrayHolder.shared.kp80) + let kp47 = identity(FixedSizeArrayHolder.shared.kp47) + let kp92 = identity(FixedSizeArrayHolder.shared.kp92) + let kp78 = identity(FixedSizeArrayHolder.shared.kp78) + let kp79 = identity(FixedSizeArrayHolder.shared.kp79) + let kp48 = identity(FixedSizeArrayHolder.shared.kp48) + let kp64 = identity(FixedSizeArrayHolder.shared.kp64) + let kp65 = identity(FixedSizeArrayHolder.shared.kp65) + let kp19 = identity(FixedSizeArrayHolder.shared.kp19) + let kp66 = identity(FixedSizeArrayHolder.shared.kp66) + let kp10 = identity(FixedSizeArrayHolder.shared.kp10) + let kp89 = identity(FixedSizeArrayHolder.shared.kp89) + let kp16 = identity(FixedSizeArrayHolder.shared.kp16) + let kp06 = identity(FixedSizeArrayHolder.shared.kp06) + let kp26 = identity(FixedSizeArrayHolder.shared.kp26) + let kp83 = identity(FixedSizeArrayHolder.shared.kp83) for t in 1 ... iterationMultipier * n { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100[keyPath: identity(kp46)] += fixedSizeArray100.element63 - fixedSizeArray100 - .element99 - fixedSizeArray100[keyPath: identity(kp43)] += fixedSizeArray100.element39 * fixedSizeArray100 - .element27 - fixedSizeArray100[keyPath: identity(kp81)] += fixedSizeArray100.element49 / fixedSizeArray100 - .element9 - fixedSizeArray100[keyPath: identity(kp31)] += fixedSizeArray100.element90 + fixedSizeArray100 - .element84 - fixedSizeArray100[keyPath: identity(kp35)] += fixedSizeArray100.element6 - fixedSizeArray100 - .element22 - fixedSizeArray100[keyPath: identity(kp14)] += fixedSizeArray100.element67 * fixedSizeArray100 - .element82 - fixedSizeArray100[keyPath: identity(kp51)] += fixedSizeArray100.element40 / fixedSizeArray100 - .element86 - fixedSizeArray100[keyPath: identity(kp24)] += fixedSizeArray100.element23 + fixedSizeArray100 - .element49 - fixedSizeArray100[keyPath: identity(kp90)] += fixedSizeArray100.element95 - fixedSizeArray100 - .element18 - fixedSizeArray100[keyPath: identity(kp80)] += fixedSizeArray100.element45 * fixedSizeArray100 - .element97 - fixedSizeArray100[keyPath: identity(kp47)] += fixedSizeArray100.element65 / fixedSizeArray100 - .element69 - fixedSizeArray100[keyPath: identity(kp92)] += fixedSizeArray100.element80 + fixedSizeArray100 - .element76 - fixedSizeArray100[keyPath: identity(kp78)] += fixedSizeArray100.element32 - fixedSizeArray100 - .element32 - fixedSizeArray100[keyPath: identity(kp79)] += fixedSizeArray100.element59 * fixedSizeArray100 - .element52 - fixedSizeArray100[keyPath: identity(kp48)] += fixedSizeArray100.element60 / fixedSizeArray100 - .element24 - fixedSizeArray100[keyPath: identity(kp64)] += fixedSizeArray100.element41 + fixedSizeArray100 - .element87 - fixedSizeArray100[keyPath: identity(kp65)] += fixedSizeArray100.element72 - fixedSizeArray100 - .element67 - fixedSizeArray100[keyPath: identity(kp19)] += fixedSizeArray100.element81 * fixedSizeArray100 - .element65 - fixedSizeArray100[keyPath: identity(kp66)] += fixedSizeArray100.element55 / fixedSizeArray100 - .element43 - fixedSizeArray100[keyPath: identity(kp10)] += fixedSizeArray100.element52 + fixedSizeArray100 - .element12 - fixedSizeArray100[keyPath: identity(kp89)] += fixedSizeArray100.element50 - fixedSizeArray100 - .element21 - fixedSizeArray100[keyPath: identity(kp16)] += fixedSizeArray100.element80 * fixedSizeArray100 - .element77 - fixedSizeArray100[keyPath: identity(kp06)] += fixedSizeArray100.element62 / fixedSizeArray100 - .element40 - fixedSizeArray100[keyPath: identity(kp26)] += fixedSizeArray100.element65 + fixedSizeArray100 - .element65 - fixedSizeArray100[keyPath: identity(kp83)] += fixedSizeArray100.element78 - fixedSizeArray100 - .element50 + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) + fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100 + .element99 + fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100 + .element27 + fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100 + .element9 + fixedSizeArray100[keyPath: kp31] += fixedSizeArray100.element90 + fixedSizeArray100 + .element84 + fixedSizeArray100[keyPath: kp35] += fixedSizeArray100.element6 - fixedSizeArray100 + .element22 + fixedSizeArray100[keyPath: kp14] += fixedSizeArray100.element67 * fixedSizeArray100 + .element82 + fixedSizeArray100[keyPath: kp51] += fixedSizeArray100.element40 / fixedSizeArray100 + .element86 + fixedSizeArray100[keyPath: kp24] += fixedSizeArray100.element23 + fixedSizeArray100 + .element49 + fixedSizeArray100[keyPath: kp90] += fixedSizeArray100.element95 - fixedSizeArray100 + .element18 + fixedSizeArray100[keyPath: kp80] += fixedSizeArray100.element45 * fixedSizeArray100 + .element97 + fixedSizeArray100[keyPath: kp47] += fixedSizeArray100.element65 / fixedSizeArray100 + .element69 + fixedSizeArray100[keyPath: kp92] += fixedSizeArray100.element80 + fixedSizeArray100 + .element76 + fixedSizeArray100[keyPath: kp78] += fixedSizeArray100.element32 - fixedSizeArray100 + .element32 + fixedSizeArray100[keyPath: kp79] += fixedSizeArray100.element59 * fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: kp48] += fixedSizeArray100.element60 / fixedSizeArray100 + .element24 + fixedSizeArray100[keyPath: kp64] += fixedSizeArray100.element41 + fixedSizeArray100 + .element87 + fixedSizeArray100[keyPath: kp65] += fixedSizeArray100.element72 - fixedSizeArray100 + .element67 + fixedSizeArray100[keyPath: kp19] += fixedSizeArray100.element81 * fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp66] += fixedSizeArray100.element55 / fixedSizeArray100 + .element43 + fixedSizeArray100[keyPath: kp10] += fixedSizeArray100.element52 + fixedSizeArray100 + .element12 + fixedSizeArray100[keyPath: kp89] += fixedSizeArray100.element50 - fixedSizeArray100 + .element21 + fixedSizeArray100[keyPath: kp16] += fixedSizeArray100.element80 * fixedSizeArray100 + .element77 + fixedSizeArray100[keyPath: kp06] += fixedSizeArray100.element62 / fixedSizeArray100 + .element40 + fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100 + .element50 } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) check(sum > ElementType(0)) @@ -1513,69 +1513,69 @@ public func run_testKeyPathSmallStruct(n: Int) { var fixedSizeArray10 = FixedSizeArrayHolder.shared.fixedSizeArray10 let iterationMultipier = 25 - let kp0 = FixedSizeArray10.getKeypathToElement(index: 0) - let kp1 = FixedSizeArray10.getKeypathToElement(index: 1) - let kp2 = FixedSizeArray10.getKeypathToElement(index: 2) - let kp3 = FixedSizeArray10.getKeypathToElement(index: 3) - let kp4 = FixedSizeArray10.getKeypathToElement(index: 4) - let kp5 = FixedSizeArray10.getKeypathToElement(index: 5) - let kp6 = FixedSizeArray10.getKeypathToElement(index: 6) - let kp7 = FixedSizeArray10.getKeypathToElement(index: 7) - let kp8 = FixedSizeArray10.getKeypathToElement(index: 8) - let kp9 = FixedSizeArray10.getKeypathToElement(index: 9) + let kp0 = identity(FixedSizeArray10.getKeypathToElement(index: 0)) + let kp1 = identity(FixedSizeArray10.getKeypathToElement(index: 1)) + let kp2 = identity(FixedSizeArray10.getKeypathToElement(index: 2)) + let kp3 = identity(FixedSizeArray10.getKeypathToElement(index: 3)) + let kp4 = identity(FixedSizeArray10.getKeypathToElement(index: 4)) + let kp5 = identity(FixedSizeArray10.getKeypathToElement(index: 5)) + let kp6 = identity(FixedSizeArray10.getKeypathToElement(index: 6)) + let kp7 = identity(FixedSizeArray10.getKeypathToElement(index: 7)) + let kp8 = identity(FixedSizeArray10.getKeypathToElement(index: 8)) + let kp9 = identity(FixedSizeArray10.getKeypathToElement(index: 9)) for t in 1 ... iterationMultipier * n { - fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) - fixedSizeArray10.element6 += fixedSizeArray10 - .element3 - fixedSizeArray10[keyPath: identity(kp9)] - fixedSizeArray10.element3 += fixedSizeArray10 - .element9 * fixedSizeArray10[keyPath: identity(kp7)] - fixedSizeArray10.element1 += fixedSizeArray10 - .element9 / fixedSizeArray10[keyPath: identity(kp9)] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 + fixedSizeArray10[keyPath: identity(kp4)] - fixedSizeArray10.element5 += fixedSizeArray10 - .element6 - fixedSizeArray10[keyPath: identity(kp2)] - fixedSizeArray10.element4 += fixedSizeArray10 - .element7 * fixedSizeArray10[keyPath: identity(kp2)] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 / fixedSizeArray10[keyPath: identity(kp6)] - fixedSizeArray10.element4 += fixedSizeArray10 - .element3 + fixedSizeArray10[keyPath: identity(kp9)] - fixedSizeArray10.element0 += fixedSizeArray10 - .element5 - fixedSizeArray10[keyPath: identity(kp8)] - fixedSizeArray10.element0 += fixedSizeArray10 - .element5 * fixedSizeArray10[keyPath: identity(kp7)] - fixedSizeArray10.element7 += fixedSizeArray10 - .element5 / fixedSizeArray10[keyPath: identity(kp9)] - fixedSizeArray10.element2 += fixedSizeArray10 - .element0 + fixedSizeArray10[keyPath: identity(kp6)] - fixedSizeArray10.element8 += fixedSizeArray10 - .element2 - fixedSizeArray10[keyPath: identity(kp2)] - fixedSizeArray10.element9 += fixedSizeArray10 - .element9 * fixedSizeArray10[keyPath: identity(kp2)] - fixedSizeArray10.element6 += fixedSizeArray10 - .element0 / fixedSizeArray10[keyPath: identity(kp4)] - fixedSizeArray10.element4 += fixedSizeArray10 - .element1 + fixedSizeArray10[keyPath: identity(kp7)] - fixedSizeArray10.element5 += fixedSizeArray10 - .element2 - fixedSizeArray10[keyPath: identity(kp7)] - fixedSizeArray10.element9 += fixedSizeArray10 - .element1 * fixedSizeArray10[keyPath: identity(kp5)] - fixedSizeArray10.element6 += fixedSizeArray10 - .element5 / fixedSizeArray10[keyPath: identity(kp3)] - fixedSizeArray10.element0 += fixedSizeArray10 - .element2 + fixedSizeArray10[keyPath: identity(kp2)] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 - fixedSizeArray10[keyPath: identity(kp1)] - fixedSizeArray10.element6 += fixedSizeArray10 - .element0 * fixedSizeArray10[keyPath: identity(kp7)] - fixedSizeArray10.element6 += fixedSizeArray10 - .element2 / fixedSizeArray10[keyPath: identity(kp0)] - fixedSizeArray10.element6 += fixedSizeArray10 - .element5 + fixedSizeArray10[keyPath: identity(kp0)] - fixedSizeArray10.element3 += fixedSizeArray10 - .element8 - fixedSizeArray10[keyPath: identity(kp0)] + fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) + fixedSizeArray10.element6 += fixedSizeArray10 + .element3 - fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element3 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element1 += fixedSizeArray10 + .element9 / fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: kp4] + fixedSizeArray10.element5 += fixedSizeArray10 + .element6 - fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element4 += fixedSizeArray10 + .element7 * fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: kp6] + fixedSizeArray10.element4 += fixedSizeArray10 + .element3 + fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 - fixedSizeArray10[keyPath: kp8] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element7 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element2 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: kp6] + fixedSizeArray10.element8 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element9 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: kp4] + fixedSizeArray10.element4 += fixedSizeArray10 + .element1 + fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element5 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element9 += fixedSizeArray10 + .element1 * fixedSizeArray10[keyPath: kp5] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: kp3] + fixedSizeArray10.element0 += fixedSizeArray10 + .element2 + fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 - fixedSizeArray10[keyPath: kp1] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element6 += fixedSizeArray10 + .element2 / fixedSizeArray10[keyPath: kp0] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 + fixedSizeArray10[keyPath: kp0] + fixedSizeArray10.element3 += fixedSizeArray10 + .element8 - fixedSizeArray10[keyPath: kp0] } let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray10) blackHole(sum) @@ -1587,13 +1587,13 @@ public func run_KeyPathMutatingGetset(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \MutatingGetsetNested1.nestedItem.nestedItem.nestedItem.nestedItem - .storage + let destinationKeyPath = identity(\MutatingGetsetNested1.nestedItem.nestedItem.nestedItem.nestedItem + .storage) let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetset.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForMutatingGetset[index] - sum += element[keyPath: identity(destinationKeyPath)] + sum += element[keyPath: destinationKeyPath] index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) @@ -1605,11 +1605,11 @@ public func run_KeyPathGet(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.storage + let destinationKeyPath = identity(\GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.storage) let elementCount = FixedSizeArrayHolder.shared.arrayForGet.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForGet[index] - sum += element[keyPath: identity(destinationKeyPath)] + sum += element[keyPath: destinationKeyPath] index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) @@ -1623,12 +1623,12 @@ public func run_KeyPathOptionals(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \Optional1._nestedItemStorage?!._nestedItemStorage?!._nestedItemStorage?! - ._nestedItemStorage?!._storage + let destinationKeyPath = identity(\Optional1._nestedItemStorage?!._nestedItemStorage?!._nestedItemStorage?! + ._nestedItemStorage?!._storage) let elementCount = FixedSizeArrayHolder.shared.arrayForOptionals.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForOptionals[index] - sum += element[keyPath: identity(destinationKeyPath)]! + sum += element[keyPath: destinationKeyPath]! index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) @@ -1640,11 +1640,11 @@ public func run_KeyPathNestedClasses(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \C1.r.r.r.r.a + let destinationKeyPath = identity(\C1.r.r.r.r.a) let elementCount = FixedSizeArrayHolder.shared.arrayForNestedClasses.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForNestedClasses[index] - sum += element[keyPath: identity(destinationKeyPath)] + sum += element[keyPath: destinationKeyPath] index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) @@ -1657,11 +1657,11 @@ public func run_KeyPathNonmutatingGetset(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = \M.n.o.p.q.q + let destinationKeyPath = identity(\M.n.o.p.q.q) let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset[index] - sum += element[keyPath: identity(destinationKeyPath)] + sum += element[keyPath: destinationKeyPath] index = (index + 1) % elementCount } check(sum == iterationMultipier * n * expectedIntForNestedItems) From 34d0ac0a66ae29a5a7a39a5d83894cd6d79f96c3 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Tue, 23 Aug 2022 11:32:30 -0600 Subject: [PATCH 12/18] Moving destinationKeyPaths into FixedSizeArrayHolder to try to reduce long setup overhead errors. --- .../KeyPathPerformanceTests.swift | 332 +++++++++--------- 1 file changed, 171 insertions(+), 161 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index ba71f6be466f1..5a0498cc33848 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -102,6 +102,11 @@ class FixedSizeArrayHolder { var arrayForOptionals: [Optional1] var arrayForNestedClasses: [C1] var arrayForNonMutatingGetset: [M] + var keypathForMutatingGetSet: WritableKeyPath + var keypathForGet: KeyPath + var keypathForOptional: KeyPath + var keypathForNestedClasses: KeyPath + var keypathForNonMutatingGetset: WritableKeyPath // Same order as in KeyPathWritePerformance var kp46: WritableKeyPath, ElementType> @@ -216,6 +221,14 @@ class FixedSizeArrayHolder { kp40 = FixedSizeArray100.getKeypathToElement(index: 40) kp60 = FixedSizeArray100.getKeypathToElement(index: 60) kp50 = FixedSizeArray100.getKeypathToElement(index: 50) + + keypathForMutatingGetSet = identity(\MutatingGetsetNested1.nestedItem.nestedItem.nestedItem + .nestedItem.storage) + keypathForGet = identity(\GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.storage) + keypathForOptional = identity(\Optional1._nestedItemStorage?!._nestedItemStorage?! + ._nestedItemStorage?!._nestedItemStorage?!._storage) + keypathForNestedClasses = identity(\C1.r.r.r.r.a) + keypathForNonMutatingGetset = identity(\M.n.o.p.q.q) } } @@ -1362,57 +1375,57 @@ public func run_testKeyPathReadPerformance(n: Int) { let kp50 = identity(FixedSizeArrayHolder.shared.kp50) for t in 1 ... iterationMultipier * n { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100.element46 += fixedSizeArray100 - .element63 - fixedSizeArray100[keyPath: kp99] - fixedSizeArray100.element43 += fixedSizeArray100 - .element39 * fixedSizeArray100[keyPath: kp27] - fixedSizeArray100.element81 += fixedSizeArray100 - .element49 / fixedSizeArray100[keyPath: kp09] - fixedSizeArray100.element31 += fixedSizeArray100 - .element90 + fixedSizeArray100[keyPath: kp84] - fixedSizeArray100.element35 += fixedSizeArray100 - .element6 - fixedSizeArray100[keyPath: kp22] - fixedSizeArray100.element14 += fixedSizeArray100 - .element67 * fixedSizeArray100[keyPath: kp82] - fixedSizeArray100.element51 += fixedSizeArray100 - .element40 / fixedSizeArray100[keyPath: kp86] - fixedSizeArray100.element24 += fixedSizeArray100 - .element23 + fixedSizeArray100[keyPath: kp49] - fixedSizeArray100.element90 += fixedSizeArray100 - .element95 - fixedSizeArray100[keyPath: kp18] - fixedSizeArray100.element80 += fixedSizeArray100 - .element45 * fixedSizeArray100[keyPath: kp97] - fixedSizeArray100.element47 += fixedSizeArray100 - .element65 / fixedSizeArray100[keyPath: kp69] - fixedSizeArray100.element92 += fixedSizeArray100 - .element80 + fixedSizeArray100[keyPath: kp76] - fixedSizeArray100.element78 += fixedSizeArray100 - .element32 - fixedSizeArray100[keyPath: kp32] - fixedSizeArray100.element79 += fixedSizeArray100 - .element59 * fixedSizeArray100[keyPath: kp52] - fixedSizeArray100.element46 += fixedSizeArray100 - .element60 / fixedSizeArray100[keyPath: kp24] - fixedSizeArray100.element64 += fixedSizeArray100 - .element41 + fixedSizeArray100[keyPath: kp87] - fixedSizeArray100.element65 += fixedSizeArray100 - .element72 - fixedSizeArray100[keyPath: kp67] - fixedSizeArray100.element19 += fixedSizeArray100 - .element81 * fixedSizeArray100[keyPath: kp65] - fixedSizeArray100.element66 += fixedSizeArray100 - .element55 / fixedSizeArray100[keyPath: kp43] - fixedSizeArray100.element10 += fixedSizeArray100 - .element52 + fixedSizeArray100[keyPath: kp12] - fixedSizeArray100.element81 += fixedSizeArray100 - .element50 - fixedSizeArray100[keyPath: kp21] - fixedSizeArray100.element16 += fixedSizeArray100 - .element80 * fixedSizeArray100[keyPath: kp77] - fixedSizeArray100.element6 += fixedSizeArray100 - .element62 / fixedSizeArray100[keyPath: kp40] - fixedSizeArray100.element26 += fixedSizeArray100 - .element65 + fixedSizeArray100[keyPath: kp60] - fixedSizeArray100.element83 += fixedSizeArray100 - .element78 - fixedSizeArray100[keyPath: kp50] + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) + fixedSizeArray100.element46 += fixedSizeArray100 + .element63 - fixedSizeArray100[keyPath: kp99] + fixedSizeArray100.element43 += fixedSizeArray100 + .element39 * fixedSizeArray100[keyPath: kp27] + fixedSizeArray100.element81 += fixedSizeArray100 + .element49 / fixedSizeArray100[keyPath: kp09] + fixedSizeArray100.element31 += fixedSizeArray100 + .element90 + fixedSizeArray100[keyPath: kp84] + fixedSizeArray100.element35 += fixedSizeArray100 + .element6 - fixedSizeArray100[keyPath: kp22] + fixedSizeArray100.element14 += fixedSizeArray100 + .element67 * fixedSizeArray100[keyPath: kp82] + fixedSizeArray100.element51 += fixedSizeArray100 + .element40 / fixedSizeArray100[keyPath: kp86] + fixedSizeArray100.element24 += fixedSizeArray100 + .element23 + fixedSizeArray100[keyPath: kp49] + fixedSizeArray100.element90 += fixedSizeArray100 + .element95 - fixedSizeArray100[keyPath: kp18] + fixedSizeArray100.element80 += fixedSizeArray100 + .element45 * fixedSizeArray100[keyPath: kp97] + fixedSizeArray100.element47 += fixedSizeArray100 + .element65 / fixedSizeArray100[keyPath: kp69] + fixedSizeArray100.element92 += fixedSizeArray100 + .element80 + fixedSizeArray100[keyPath: kp76] + fixedSizeArray100.element78 += fixedSizeArray100 + .element32 - fixedSizeArray100[keyPath: kp32] + fixedSizeArray100.element79 += fixedSizeArray100 + .element59 * fixedSizeArray100[keyPath: kp52] + fixedSizeArray100.element46 += fixedSizeArray100 + .element60 / fixedSizeArray100[keyPath: kp24] + fixedSizeArray100.element64 += fixedSizeArray100 + .element41 + fixedSizeArray100[keyPath: kp87] + fixedSizeArray100.element65 += fixedSizeArray100 + .element72 - fixedSizeArray100[keyPath: kp67] + fixedSizeArray100.element19 += fixedSizeArray100 + .element81 * fixedSizeArray100[keyPath: kp65] + fixedSizeArray100.element66 += fixedSizeArray100 + .element55 / fixedSizeArray100[keyPath: kp43] + fixedSizeArray100.element10 += fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: kp12] + fixedSizeArray100.element81 += fixedSizeArray100 + .element50 - fixedSizeArray100[keyPath: kp21] + fixedSizeArray100.element16 += fixedSizeArray100 + .element80 * fixedSizeArray100[keyPath: kp77] + fixedSizeArray100.element6 += fixedSizeArray100 + .element62 / fixedSizeArray100[keyPath: kp40] + fixedSizeArray100.element26 += fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp60] + fixedSizeArray100.element83 += fixedSizeArray100 + .element78 - fixedSizeArray100[keyPath: kp50] } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) check(sum > ElementType(0)) @@ -1451,57 +1464,57 @@ public func run_testKeyPathWritePerformance(n: Int) { let kp83 = identity(FixedSizeArrayHolder.shared.kp83) for t in 1 ... iterationMultipier * n { - fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) - fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100 - .element99 - fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100 - .element27 - fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100 - .element9 - fixedSizeArray100[keyPath: kp31] += fixedSizeArray100.element90 + fixedSizeArray100 - .element84 - fixedSizeArray100[keyPath: kp35] += fixedSizeArray100.element6 - fixedSizeArray100 - .element22 - fixedSizeArray100[keyPath: kp14] += fixedSizeArray100.element67 * fixedSizeArray100 - .element82 - fixedSizeArray100[keyPath: kp51] += fixedSizeArray100.element40 / fixedSizeArray100 - .element86 - fixedSizeArray100[keyPath: kp24] += fixedSizeArray100.element23 + fixedSizeArray100 - .element49 - fixedSizeArray100[keyPath: kp90] += fixedSizeArray100.element95 - fixedSizeArray100 - .element18 - fixedSizeArray100[keyPath: kp80] += fixedSizeArray100.element45 * fixedSizeArray100 - .element97 - fixedSizeArray100[keyPath: kp47] += fixedSizeArray100.element65 / fixedSizeArray100 - .element69 - fixedSizeArray100[keyPath: kp92] += fixedSizeArray100.element80 + fixedSizeArray100 - .element76 - fixedSizeArray100[keyPath: kp78] += fixedSizeArray100.element32 - fixedSizeArray100 - .element32 - fixedSizeArray100[keyPath: kp79] += fixedSizeArray100.element59 * fixedSizeArray100 - .element52 - fixedSizeArray100[keyPath: kp48] += fixedSizeArray100.element60 / fixedSizeArray100 - .element24 - fixedSizeArray100[keyPath: kp64] += fixedSizeArray100.element41 + fixedSizeArray100 - .element87 - fixedSizeArray100[keyPath: kp65] += fixedSizeArray100.element72 - fixedSizeArray100 - .element67 - fixedSizeArray100[keyPath: kp19] += fixedSizeArray100.element81 * fixedSizeArray100 - .element65 - fixedSizeArray100[keyPath: kp66] += fixedSizeArray100.element55 / fixedSizeArray100 - .element43 - fixedSizeArray100[keyPath: kp10] += fixedSizeArray100.element52 + fixedSizeArray100 - .element12 - fixedSizeArray100[keyPath: kp89] += fixedSizeArray100.element50 - fixedSizeArray100 - .element21 - fixedSizeArray100[keyPath: kp16] += fixedSizeArray100.element80 * fixedSizeArray100 - .element77 - fixedSizeArray100[keyPath: kp06] += fixedSizeArray100.element62 / fixedSizeArray100 - .element40 - fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100 - .element65 - fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100 - .element50 + fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) + fixedSizeArray100[keyPath: kp46] += fixedSizeArray100.element63 - fixedSizeArray100 + .element99 + fixedSizeArray100[keyPath: kp43] += fixedSizeArray100.element39 * fixedSizeArray100 + .element27 + fixedSizeArray100[keyPath: kp81] += fixedSizeArray100.element49 / fixedSizeArray100 + .element9 + fixedSizeArray100[keyPath: kp31] += fixedSizeArray100.element90 + fixedSizeArray100 + .element84 + fixedSizeArray100[keyPath: kp35] += fixedSizeArray100.element6 - fixedSizeArray100 + .element22 + fixedSizeArray100[keyPath: kp14] += fixedSizeArray100.element67 * fixedSizeArray100 + .element82 + fixedSizeArray100[keyPath: kp51] += fixedSizeArray100.element40 / fixedSizeArray100 + .element86 + fixedSizeArray100[keyPath: kp24] += fixedSizeArray100.element23 + fixedSizeArray100 + .element49 + fixedSizeArray100[keyPath: kp90] += fixedSizeArray100.element95 - fixedSizeArray100 + .element18 + fixedSizeArray100[keyPath: kp80] += fixedSizeArray100.element45 * fixedSizeArray100 + .element97 + fixedSizeArray100[keyPath: kp47] += fixedSizeArray100.element65 / fixedSizeArray100 + .element69 + fixedSizeArray100[keyPath: kp92] += fixedSizeArray100.element80 + fixedSizeArray100 + .element76 + fixedSizeArray100[keyPath: kp78] += fixedSizeArray100.element32 - fixedSizeArray100 + .element32 + fixedSizeArray100[keyPath: kp79] += fixedSizeArray100.element59 * fixedSizeArray100 + .element52 + fixedSizeArray100[keyPath: kp48] += fixedSizeArray100.element60 / fixedSizeArray100 + .element24 + fixedSizeArray100[keyPath: kp64] += fixedSizeArray100.element41 + fixedSizeArray100 + .element87 + fixedSizeArray100[keyPath: kp65] += fixedSizeArray100.element72 - fixedSizeArray100 + .element67 + fixedSizeArray100[keyPath: kp19] += fixedSizeArray100.element81 * fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp66] += fixedSizeArray100.element55 / fixedSizeArray100 + .element43 + fixedSizeArray100[keyPath: kp10] += fixedSizeArray100.element52 + fixedSizeArray100 + .element12 + fixedSizeArray100[keyPath: kp89] += fixedSizeArray100.element50 - fixedSizeArray100 + .element21 + fixedSizeArray100[keyPath: kp16] += fixedSizeArray100.element80 * fixedSizeArray100 + .element77 + fixedSizeArray100[keyPath: kp06] += fixedSizeArray100.element62 / fixedSizeArray100 + .element40 + fixedSizeArray100[keyPath: kp26] += fixedSizeArray100.element65 + fixedSizeArray100 + .element65 + fixedSizeArray100[keyPath: kp83] += fixedSizeArray100.element78 - fixedSizeArray100 + .element50 } let sum = computeSumOfFixedSizeArray100(fixedSizeArray100: &fixedSizeArray100) check(sum > ElementType(0)) @@ -1525,57 +1538,57 @@ public func run_testKeyPathSmallStruct(n: Int) { let kp9 = identity(FixedSizeArray10.getKeypathToElement(index: 9)) for t in 1 ... iterationMultipier * n { - fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) - fixedSizeArray10.element6 += fixedSizeArray10 - .element3 - fixedSizeArray10[keyPath: kp9] - fixedSizeArray10.element3 += fixedSizeArray10 - .element9 * fixedSizeArray10[keyPath: kp7] - fixedSizeArray10.element1 += fixedSizeArray10 - .element9 / fixedSizeArray10[keyPath: kp9] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 + fixedSizeArray10[keyPath: kp4] - fixedSizeArray10.element5 += fixedSizeArray10 - .element6 - fixedSizeArray10[keyPath: kp2] - fixedSizeArray10.element4 += fixedSizeArray10 - .element7 * fixedSizeArray10[keyPath: kp2] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 / fixedSizeArray10[keyPath: kp6] - fixedSizeArray10.element4 += fixedSizeArray10 - .element3 + fixedSizeArray10[keyPath: kp9] - fixedSizeArray10.element0 += fixedSizeArray10 - .element5 - fixedSizeArray10[keyPath: kp8] - fixedSizeArray10.element0 += fixedSizeArray10 - .element5 * fixedSizeArray10[keyPath: kp7] - fixedSizeArray10.element7 += fixedSizeArray10 - .element5 / fixedSizeArray10[keyPath: kp9] - fixedSizeArray10.element2 += fixedSizeArray10 - .element0 + fixedSizeArray10[keyPath: kp6] - fixedSizeArray10.element8 += fixedSizeArray10 - .element2 - fixedSizeArray10[keyPath: kp2] - fixedSizeArray10.element9 += fixedSizeArray10 - .element9 * fixedSizeArray10[keyPath: kp2] - fixedSizeArray10.element6 += fixedSizeArray10 - .element0 / fixedSizeArray10[keyPath: kp4] - fixedSizeArray10.element4 += fixedSizeArray10 - .element1 + fixedSizeArray10[keyPath: kp7] - fixedSizeArray10.element5 += fixedSizeArray10 - .element2 - fixedSizeArray10[keyPath: kp7] - fixedSizeArray10.element9 += fixedSizeArray10 - .element1 * fixedSizeArray10[keyPath: kp5] - fixedSizeArray10.element6 += fixedSizeArray10 - .element5 / fixedSizeArray10[keyPath: kp3] - fixedSizeArray10.element0 += fixedSizeArray10 - .element2 + fixedSizeArray10[keyPath: kp2] - fixedSizeArray10.element1 += fixedSizeArray10 - .element0 - fixedSizeArray10[keyPath: kp1] - fixedSizeArray10.element6 += fixedSizeArray10 - .element0 * fixedSizeArray10[keyPath: kp7] - fixedSizeArray10.element6 += fixedSizeArray10 - .element2 / fixedSizeArray10[keyPath: kp0] - fixedSizeArray10.element6 += fixedSizeArray10 - .element5 + fixedSizeArray10[keyPath: kp0] - fixedSizeArray10.element3 += fixedSizeArray10 - .element8 - fixedSizeArray10[keyPath: kp0] + fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) + fixedSizeArray10.element6 += fixedSizeArray10 + .element3 - fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element3 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element1 += fixedSizeArray10 + .element9 / fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: kp4] + fixedSizeArray10.element5 += fixedSizeArray10 + .element6 - fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element4 += fixedSizeArray10 + .element7 * fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: kp6] + fixedSizeArray10.element4 += fixedSizeArray10 + .element3 + fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 - fixedSizeArray10[keyPath: kp8] + fixedSizeArray10.element0 += fixedSizeArray10 + .element5 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element7 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: kp9] + fixedSizeArray10.element2 += fixedSizeArray10 + .element0 + fixedSizeArray10[keyPath: kp6] + fixedSizeArray10.element8 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element9 += fixedSizeArray10 + .element9 * fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 / fixedSizeArray10[keyPath: kp4] + fixedSizeArray10.element4 += fixedSizeArray10 + .element1 + fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element5 += fixedSizeArray10 + .element2 - fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element9 += fixedSizeArray10 + .element1 * fixedSizeArray10[keyPath: kp5] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 / fixedSizeArray10[keyPath: kp3] + fixedSizeArray10.element0 += fixedSizeArray10 + .element2 + fixedSizeArray10[keyPath: kp2] + fixedSizeArray10.element1 += fixedSizeArray10 + .element0 - fixedSizeArray10[keyPath: kp1] + fixedSizeArray10.element6 += fixedSizeArray10 + .element0 * fixedSizeArray10[keyPath: kp7] + fixedSizeArray10.element6 += fixedSizeArray10 + .element2 / fixedSizeArray10[keyPath: kp0] + fixedSizeArray10.element6 += fixedSizeArray10 + .element5 + fixedSizeArray10[keyPath: kp0] + fixedSizeArray10.element3 += fixedSizeArray10 + .element8 - fixedSizeArray10[keyPath: kp0] } let sum = computeSumOfFixedSizeArray10(fixedSizeArray10: &fixedSizeArray10) blackHole(sum) @@ -1587,9 +1600,7 @@ public func run_KeyPathMutatingGetset(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = identity(\MutatingGetsetNested1.nestedItem.nestedItem.nestedItem.nestedItem - .storage) - + let destinationKeyPath = FixedSizeArrayHolder.shared.keypathForMutatingGetSet let elementCount = FixedSizeArrayHolder.shared.arrayForMutatingGetset.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForMutatingGetset[index] @@ -1605,7 +1616,7 @@ public func run_KeyPathGet(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = identity(\GetNested1.nestedItem.nestedItem.nestedItem.nestedItem.storage) + let destinationKeyPath = FixedSizeArrayHolder.shared.keypathForGet let elementCount = FixedSizeArrayHolder.shared.arrayForGet.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForGet[index] @@ -1623,8 +1634,7 @@ public func run_KeyPathOptionals(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = identity(\Optional1._nestedItemStorage?!._nestedItemStorage?!._nestedItemStorage?! - ._nestedItemStorage?!._storage) + let destinationKeyPath = FixedSizeArrayHolder.shared.keypathForOptional let elementCount = FixedSizeArrayHolder.shared.arrayForOptionals.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForOptionals[index] @@ -1640,7 +1650,7 @@ public func run_KeyPathNestedClasses(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = identity(\C1.r.r.r.r.a) + let destinationKeyPath = FixedSizeArrayHolder.shared.keypathForNestedClasses let elementCount = FixedSizeArrayHolder.shared.arrayForNestedClasses.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForNestedClasses[index] @@ -1657,7 +1667,7 @@ public func run_KeyPathNonmutatingGetset(n: Int) { var index = 0 let iterationMultipier = 200 - let destinationKeyPath = identity(\M.n.o.p.q.q) + let destinationKeyPath = FixedSizeArrayHolder.shared.keypathForNonMutatingGetset let elementCount = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset.count for _ in 1 ... iterationMultipier * n { let element = FixedSizeArrayHolder.shared.arrayForNonMutatingGetset[index] From ceb69d0c08e8e1df12a110e8b2341f9224626e61 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Tue, 23 Aug 2022 13:31:37 -0600 Subject: [PATCH 13/18] Additional moving of the identity() wrapping into the singleton's init() to try to reduce setup time errors. --- .../KeyPathPerformanceTests.swift | 245 ++++++++++-------- 1 file changed, 134 insertions(+), 111 deletions(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 5a0498cc33848..f1d5c69c9347f 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -159,6 +159,18 @@ class FixedSizeArrayHolder { var kp60: WritableKeyPath, ElementType> var kp50: WritableKeyPath, ElementType> + // Same order as in KeyPathSmallStruct. + var kp0: WritableKeyPath, ElementType> + var kp1: WritableKeyPath, ElementType> + var kp2: WritableKeyPath, ElementType> + var kp3: WritableKeyPath, ElementType> + var kp4: WritableKeyPath, ElementType> + var kp5: WritableKeyPath, ElementType> + var kp6: WritableKeyPath, ElementType> + var kp7: WritableKeyPath, ElementType> + var kp8: WritableKeyPath, ElementType> + var kp9: WritableKeyPath, ElementType> + static let shared = FixedSizeArrayHolder() init() { fixedSizeArray100 = initializeFixedSizeArray100() @@ -170,57 +182,68 @@ class FixedSizeArrayHolder { arrayForNestedClasses = [C1]() arrayForNonMutatingGetset = [M]() - kp46 = FixedSizeArray100.getKeypathToElement(index: 46) - kp43 = FixedSizeArray100.getKeypathToElement(index: 43) - kp81 = FixedSizeArray100.getKeypathToElement(index: 81) - kp31 = FixedSizeArray100.getKeypathToElement(index: 31) - kp35 = FixedSizeArray100.getKeypathToElement(index: 35) - kp14 = FixedSizeArray100.getKeypathToElement(index: 14) - kp51 = FixedSizeArray100.getKeypathToElement(index: 51) - kp24 = FixedSizeArray100.getKeypathToElement(index: 24) - kp90 = FixedSizeArray100.getKeypathToElement(index: 90) - kp80 = FixedSizeArray100.getKeypathToElement(index: 80) - kp47 = FixedSizeArray100.getKeypathToElement(index: 47) - kp92 = FixedSizeArray100.getKeypathToElement(index: 92) - kp78 = FixedSizeArray100.getKeypathToElement(index: 78) - kp79 = FixedSizeArray100.getKeypathToElement(index: 79) - kp48 = FixedSizeArray100.getKeypathToElement(index: 48) - kp64 = FixedSizeArray100.getKeypathToElement(index: 64) - kp65 = FixedSizeArray100.getKeypathToElement(index: 65) - kp19 = FixedSizeArray100.getKeypathToElement(index: 19) - kp66 = FixedSizeArray100.getKeypathToElement(index: 66) - kp10 = FixedSizeArray100.getKeypathToElement(index: 10) - kp89 = FixedSizeArray100.getKeypathToElement(index: 89) - kp16 = FixedSizeArray100.getKeypathToElement(index: 16) - kp06 = FixedSizeArray100.getKeypathToElement(index: 06) - kp26 = FixedSizeArray100.getKeypathToElement(index: 26) - kp83 = FixedSizeArray100.getKeypathToElement(index: 83) - - kp99 = FixedSizeArray100.getKeypathToElement(index: 99) - kp27 = FixedSizeArray100.getKeypathToElement(index: 27) - kp09 = FixedSizeArray100.getKeypathToElement(index: 09) - kp84 = FixedSizeArray100.getKeypathToElement(index: 84) - kp22 = FixedSizeArray100.getKeypathToElement(index: 22) - kp82 = FixedSizeArray100.getKeypathToElement(index: 82) - kp86 = FixedSizeArray100.getKeypathToElement(index: 86) - kp49 = FixedSizeArray100.getKeypathToElement(index: 49) - kp18 = FixedSizeArray100.getKeypathToElement(index: 18) - kp97 = FixedSizeArray100.getKeypathToElement(index: 97) - kp69 = FixedSizeArray100.getKeypathToElement(index: 69) - kp76 = FixedSizeArray100.getKeypathToElement(index: 76) - kp32 = FixedSizeArray100.getKeypathToElement(index: 32) - kp52 = FixedSizeArray100.getKeypathToElement(index: 52) - kp24 = FixedSizeArray100.getKeypathToElement(index: 24) - kp87 = FixedSizeArray100.getKeypathToElement(index: 87) - kp67 = FixedSizeArray100.getKeypathToElement(index: 67) - kp65 = FixedSizeArray100.getKeypathToElement(index: 65) - kp43 = FixedSizeArray100.getKeypathToElement(index: 43) - kp12 = FixedSizeArray100.getKeypathToElement(index: 12) - kp21 = FixedSizeArray100.getKeypathToElement(index: 21) - kp77 = FixedSizeArray100.getKeypathToElement(index: 77) - kp40 = FixedSizeArray100.getKeypathToElement(index: 40) - kp60 = FixedSizeArray100.getKeypathToElement(index: 60) - kp50 = FixedSizeArray100.getKeypathToElement(index: 50) + kp46 = identity(FixedSizeArray100.getKeypathToElement(index: 46)) + kp43 = identity(FixedSizeArray100.getKeypathToElement(index: 43)) + kp81 = identity(FixedSizeArray100.getKeypathToElement(index: 81)) + kp31 = identity(FixedSizeArray100.getKeypathToElement(index: 31)) + kp35 = identity(FixedSizeArray100.getKeypathToElement(index: 35)) + kp14 = identity(FixedSizeArray100.getKeypathToElement(index: 14)) + kp51 = identity(FixedSizeArray100.getKeypathToElement(index: 51)) + kp24 = identity(FixedSizeArray100.getKeypathToElement(index: 24)) + kp90 = identity(FixedSizeArray100.getKeypathToElement(index: 90)) + kp80 = identity(FixedSizeArray100.getKeypathToElement(index: 80)) + kp47 = identity(FixedSizeArray100.getKeypathToElement(index: 47)) + kp92 = identity(FixedSizeArray100.getKeypathToElement(index: 92)) + kp78 = identity(FixedSizeArray100.getKeypathToElement(index: 78)) + kp79 = identity(FixedSizeArray100.getKeypathToElement(index: 79)) + kp48 = identity(FixedSizeArray100.getKeypathToElement(index: 48)) + kp64 = identity(FixedSizeArray100.getKeypathToElement(index: 64)) + kp65 = identity(FixedSizeArray100.getKeypathToElement(index: 65)) + kp19 = identity(FixedSizeArray100.getKeypathToElement(index: 19)) + kp66 = identity(FixedSizeArray100.getKeypathToElement(index: 66)) + kp10 = identity(FixedSizeArray100.getKeypathToElement(index: 10)) + kp89 = identity(FixedSizeArray100.getKeypathToElement(index: 89)) + kp16 = identity(FixedSizeArray100.getKeypathToElement(index: 16)) + kp06 = identity(FixedSizeArray100.getKeypathToElement(index: 06)) + kp26 = identity(FixedSizeArray100.getKeypathToElement(index: 26)) + kp83 = identity(FixedSizeArray100.getKeypathToElement(index: 83)) + + kp99 = identity(FixedSizeArray100.getKeypathToElement(index: 99)) + kp27 = identity(FixedSizeArray100.getKeypathToElement(index: 27)) + kp09 = identity(FixedSizeArray100.getKeypathToElement(index: 09)) + kp84 = identity(FixedSizeArray100.getKeypathToElement(index: 84)) + kp22 = identity(FixedSizeArray100.getKeypathToElement(index: 22)) + kp82 = identity(FixedSizeArray100.getKeypathToElement(index: 82)) + kp86 = identity(FixedSizeArray100.getKeypathToElement(index: 86)) + kp49 = identity(FixedSizeArray100.getKeypathToElement(index: 49)) + kp18 = identity(FixedSizeArray100.getKeypathToElement(index: 18)) + kp97 = identity(FixedSizeArray100.getKeypathToElement(index: 97)) + kp69 = identity(FixedSizeArray100.getKeypathToElement(index: 69)) + kp76 = identity(FixedSizeArray100.getKeypathToElement(index: 76)) + kp32 = identity(FixedSizeArray100.getKeypathToElement(index: 32)) + kp52 = identity(FixedSizeArray100.getKeypathToElement(index: 52)) + kp24 = identity(FixedSizeArray100.getKeypathToElement(index: 24)) + kp87 = identity(FixedSizeArray100.getKeypathToElement(index: 87)) + kp67 = identity(FixedSizeArray100.getKeypathToElement(index: 67)) + kp65 = identity(FixedSizeArray100.getKeypathToElement(index: 65)) + kp43 = identity(FixedSizeArray100.getKeypathToElement(index: 43)) + kp12 = identity(FixedSizeArray100.getKeypathToElement(index: 12)) + kp21 = identity(FixedSizeArray100.getKeypathToElement(index: 21)) + kp77 = identity(FixedSizeArray100.getKeypathToElement(index: 77)) + kp40 = identity(FixedSizeArray100.getKeypathToElement(index: 40)) + kp60 = identity(FixedSizeArray100.getKeypathToElement(index: 60)) + kp50 = identity(FixedSizeArray100.getKeypathToElement(index: 50)) + + kp0 = identity(FixedSizeArray10.getKeypathToElement(index: 0)) + kp1 = identity(FixedSizeArray10.getKeypathToElement(index: 1)) + kp2 = identity(FixedSizeArray10.getKeypathToElement(index: 2)) + kp3 = identity(FixedSizeArray10.getKeypathToElement(index: 3)) + kp4 = identity(FixedSizeArray10.getKeypathToElement(index: 4)) + kp5 = identity(FixedSizeArray10.getKeypathToElement(index: 5)) + kp6 = identity(FixedSizeArray10.getKeypathToElement(index: 6)) + kp7 = identity(FixedSizeArray10.getKeypathToElement(index: 7)) + kp8 = identity(FixedSizeArray10.getKeypathToElement(index: 8)) + kp9 = identity(FixedSizeArray10.getKeypathToElement(index: 9)) keypathForMutatingGetSet = identity(\MutatingGetsetNested1.nestedItem.nestedItem.nestedItem .nestedItem.storage) @@ -1348,31 +1371,31 @@ public func run_testKeyPathReadPerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 25 - let kp99 = identity(FixedSizeArrayHolder.shared.kp99) - let kp27 = identity(FixedSizeArrayHolder.shared.kp27) - let kp09 = identity(FixedSizeArrayHolder.shared.kp09) - let kp84 = identity(FixedSizeArrayHolder.shared.kp84) - let kp22 = identity(FixedSizeArrayHolder.shared.kp22) - let kp82 = identity(FixedSizeArrayHolder.shared.kp82) - let kp86 = identity(FixedSizeArrayHolder.shared.kp86) - let kp49 = identity(FixedSizeArrayHolder.shared.kp49) - let kp18 = identity(FixedSizeArrayHolder.shared.kp18) - let kp97 = identity(FixedSizeArrayHolder.shared.kp97) - let kp69 = identity(FixedSizeArrayHolder.shared.kp69) - let kp76 = identity(FixedSizeArrayHolder.shared.kp76) - let kp32 = identity(FixedSizeArrayHolder.shared.kp32) - let kp52 = identity(FixedSizeArrayHolder.shared.kp52) - let kp24 = identity(FixedSizeArrayHolder.shared.kp24) - let kp87 = identity(FixedSizeArrayHolder.shared.kp87) - let kp67 = identity(FixedSizeArrayHolder.shared.kp67) - let kp65 = identity(FixedSizeArrayHolder.shared.kp65) - let kp43 = identity(FixedSizeArrayHolder.shared.kp43) - let kp12 = identity(FixedSizeArrayHolder.shared.kp12) - let kp21 = identity(FixedSizeArrayHolder.shared.kp21) - let kp77 = identity(FixedSizeArrayHolder.shared.kp77) - let kp40 = identity(FixedSizeArrayHolder.shared.kp40) - let kp60 = identity(FixedSizeArrayHolder.shared.kp60) - let kp50 = identity(FixedSizeArrayHolder.shared.kp50) + let kp99 = FixedSizeArrayHolder.shared.kp99 + let kp27 = FixedSizeArrayHolder.shared.kp27 + let kp09 = FixedSizeArrayHolder.shared.kp09 + let kp84 = FixedSizeArrayHolder.shared.kp84 + let kp22 = FixedSizeArrayHolder.shared.kp22 + let kp82 = FixedSizeArrayHolder.shared.kp82 + let kp86 = FixedSizeArrayHolder.shared.kp86 + let kp49 = FixedSizeArrayHolder.shared.kp49 + let kp18 = FixedSizeArrayHolder.shared.kp18 + let kp97 = FixedSizeArrayHolder.shared.kp97 + let kp69 = FixedSizeArrayHolder.shared.kp69 + let kp76 = FixedSizeArrayHolder.shared.kp76 + let kp32 = FixedSizeArrayHolder.shared.kp32 + let kp52 = FixedSizeArrayHolder.shared.kp52 + let kp24 = FixedSizeArrayHolder.shared.kp24 + let kp87 = FixedSizeArrayHolder.shared.kp87 + let kp67 = FixedSizeArrayHolder.shared.kp67 + let kp65 = FixedSizeArrayHolder.shared.kp65 + let kp43 = FixedSizeArrayHolder.shared.kp43 + let kp12 = FixedSizeArrayHolder.shared.kp12 + let kp21 = FixedSizeArrayHolder.shared.kp21 + let kp77 = FixedSizeArrayHolder.shared.kp77 + let kp40 = FixedSizeArrayHolder.shared.kp40 + let kp60 = FixedSizeArrayHolder.shared.kp60 + let kp50 = FixedSizeArrayHolder.shared.kp50 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) @@ -1437,31 +1460,31 @@ public func run_testKeyPathWritePerformance(n: Int) { var fixedSizeArray100 = FixedSizeArrayHolder.shared.fixedSizeArray100 let iterationMultipier = 150 - let kp46 = identity(FixedSizeArrayHolder.shared.kp46) - let kp43 = identity(FixedSizeArrayHolder.shared.kp43) - let kp81 = identity(FixedSizeArrayHolder.shared.kp81) - let kp31 = identity(FixedSizeArrayHolder.shared.kp31) - let kp35 = identity(FixedSizeArrayHolder.shared.kp35) - let kp14 = identity(FixedSizeArrayHolder.shared.kp14) - let kp51 = identity(FixedSizeArrayHolder.shared.kp51) - let kp24 = identity(FixedSizeArrayHolder.shared.kp24) - let kp90 = identity(FixedSizeArrayHolder.shared.kp90) - let kp80 = identity(FixedSizeArrayHolder.shared.kp80) - let kp47 = identity(FixedSizeArrayHolder.shared.kp47) - let kp92 = identity(FixedSizeArrayHolder.shared.kp92) - let kp78 = identity(FixedSizeArrayHolder.shared.kp78) - let kp79 = identity(FixedSizeArrayHolder.shared.kp79) - let kp48 = identity(FixedSizeArrayHolder.shared.kp48) - let kp64 = identity(FixedSizeArrayHolder.shared.kp64) - let kp65 = identity(FixedSizeArrayHolder.shared.kp65) - let kp19 = identity(FixedSizeArrayHolder.shared.kp19) - let kp66 = identity(FixedSizeArrayHolder.shared.kp66) - let kp10 = identity(FixedSizeArrayHolder.shared.kp10) - let kp89 = identity(FixedSizeArrayHolder.shared.kp89) - let kp16 = identity(FixedSizeArrayHolder.shared.kp16) - let kp06 = identity(FixedSizeArrayHolder.shared.kp06) - let kp26 = identity(FixedSizeArrayHolder.shared.kp26) - let kp83 = identity(FixedSizeArrayHolder.shared.kp83) + let kp46 = FixedSizeArrayHolder.shared.kp46 + let kp43 = FixedSizeArrayHolder.shared.kp43 + let kp81 = FixedSizeArrayHolder.shared.kp81 + let kp31 = FixedSizeArrayHolder.shared.kp31 + let kp35 = FixedSizeArrayHolder.shared.kp35 + let kp14 = FixedSizeArrayHolder.shared.kp14 + let kp51 = FixedSizeArrayHolder.shared.kp51 + let kp24 = FixedSizeArrayHolder.shared.kp24 + let kp90 = FixedSizeArrayHolder.shared.kp90 + let kp80 = FixedSizeArrayHolder.shared.kp80 + let kp47 = FixedSizeArrayHolder.shared.kp47 + let kp92 = FixedSizeArrayHolder.shared.kp92 + let kp78 = FixedSizeArrayHolder.shared.kp78 + let kp79 = FixedSizeArrayHolder.shared.kp79 + let kp48 = FixedSizeArrayHolder.shared.kp48 + let kp64 = FixedSizeArrayHolder.shared.kp64 + let kp65 = FixedSizeArrayHolder.shared.kp65 + let kp19 = FixedSizeArrayHolder.shared.kp19 + let kp66 = FixedSizeArrayHolder.shared.kp66 + let kp10 = FixedSizeArrayHolder.shared.kp10 + let kp89 = FixedSizeArrayHolder.shared.kp89 + let kp16 = FixedSizeArrayHolder.shared.kp16 + let kp06 = FixedSizeArrayHolder.shared.kp06 + let kp26 = FixedSizeArrayHolder.shared.kp26 + let kp83 = FixedSizeArrayHolder.shared.kp83 for t in 1 ... iterationMultipier * n { fixedSizeArray100.element50 += fixedSizeArray100.element1 + ElementType(t) @@ -1526,16 +1549,16 @@ public func run_testKeyPathSmallStruct(n: Int) { var fixedSizeArray10 = FixedSizeArrayHolder.shared.fixedSizeArray10 let iterationMultipier = 25 - let kp0 = identity(FixedSizeArray10.getKeypathToElement(index: 0)) - let kp1 = identity(FixedSizeArray10.getKeypathToElement(index: 1)) - let kp2 = identity(FixedSizeArray10.getKeypathToElement(index: 2)) - let kp3 = identity(FixedSizeArray10.getKeypathToElement(index: 3)) - let kp4 = identity(FixedSizeArray10.getKeypathToElement(index: 4)) - let kp5 = identity(FixedSizeArray10.getKeypathToElement(index: 5)) - let kp6 = identity(FixedSizeArray10.getKeypathToElement(index: 6)) - let kp7 = identity(FixedSizeArray10.getKeypathToElement(index: 7)) - let kp8 = identity(FixedSizeArray10.getKeypathToElement(index: 8)) - let kp9 = identity(FixedSizeArray10.getKeypathToElement(index: 9)) + let kp0 = FixedSizeArrayHolder.shared.kp0 + let kp1 = FixedSizeArrayHolder.shared.kp1 + let kp2 = FixedSizeArrayHolder.shared.kp2 + let kp3 = FixedSizeArrayHolder.shared.kp3 + let kp4 = FixedSizeArrayHolder.shared.kp4 + let kp5 = FixedSizeArrayHolder.shared.kp5 + let kp6 = FixedSizeArrayHolder.shared.kp6 + let kp7 = FixedSizeArrayHolder.shared.kp7 + let kp8 = FixedSizeArrayHolder.shared.kp8 + let kp9 = FixedSizeArrayHolder.shared.kp9 for t in 1 ... iterationMultipier * n { fixedSizeArray10.element0 += fixedSizeArray10.element1 + ElementType(t) From 66c6f7de86637e938966cf303fb3f3c5179b04c4 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Wed, 24 Aug 2022 16:16:45 -0600 Subject: [PATCH 14/18] Proposed design for skipping of KeyPath projections across trivially-typed memory. --- stdlib/public/core/KeyPath.swift | 153 ++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 24 deletions(-) diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 30d99bae7ff25..5c523ef65881f 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -35,6 +35,8 @@ internal func _abstract( /// type. @_objcRuntimeName(_TtCs11_AnyKeyPath) public class AnyKeyPath: Hashable, _AppendKeyPath { + internal var _isPureStructKeyPath: Bool? + internal var _pureStructValueOffset: Int = 0 /// The root type for this key path. @inlinable public static var rootType: Any.Type { @@ -150,15 +152,15 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { ) -> Self { _internalInvariant(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") - let result = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, + let keypath = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, Int32.self) - result._kvcKeyPathStringPtr = nil - let base = UnsafeMutableRawPointer(Builtin.projectTailElems(result, + keypath._kvcKeyPathStringPtr = nil + let base = UnsafeMutableRawPointer(Builtin.projectTailElems(keypath, Int32.self)) body(UnsafeMutableRawBufferPointer(start: base, count: bytes)) - return result + keypath._computeOffsetForPureStructKeypath() + return keypath } - final internal func withBuffer(_ f: (KeyPathBuffer) throws -> T) rethrows -> T { defer { _fixLifetime(self) } @@ -166,31 +168,94 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { return try f(KeyPathBuffer(base: base)) } - @usableFromInline // Exposed as public API by MemoryLayout.offset(of:) - internal var _storedInlineOffset: Int? { - return withBuffer { - var buffer = $0 + public var isPureStructKeyPath: Bool { + return _isPureStructKeyPath ?? false + } - // The identity key path is effectively a stored keypath of type Self - // at offset zero - if buffer.data.isEmpty { return 0 } + internal func isClass(_ item: Any.Type) -> Bool { + // Displays "warning: 'is' test is always true" at compile time, but that's not actually the case. + return (item is AnyObject) + } - var offset = 0 - while true { - let (rawComponent, optNextType) = buffer.next() - switch rawComponent.header.kind { - case .struct: - offset += rawComponent._structOrClassOffset + // TODO: Find a quicker way to see if this is a tuple. + internal func isTuple(_ item: Any) -> Bool { + // Unwraps type information if possible. + // Otherwise, everything the Mirror sees would be "Optional". + func unwrapType(_ any: T) -> Any { + let mirror = Mirror(reflecting: any) + guard mirror.displayStyle == .optional, let first = mirror.children.first else { + return any + } + return first.value + } - case .class, .computed, .optionalChain, .optionalForce, .optionalWrap, .external: - return .none + let mirror = Mirror(reflecting: unwrapType(item)) + let description = mirror.description + let offsetOfFirstBracketForMirrorDescriptionOfTuple = 11 + let idx = description.index(description.startIndex, offsetBy: offsetOfFirstBracketForMirrorDescriptionOfTuple) + if description[idx] == "(" { + return true + } + return false + } + + // If this keypath traverses structs only, it'll have a predictable memory layout. + // We can then jump to the value directly in _projectReadOnly(). + internal func _computeOffsets() -> (offset: Int, isPureStruct: Bool, isTuple: Bool) { + _pureStructValueOffset = 0 + var isPureStruct = true + var _isTuple = false + defer { + _isPureStructKeyPath = isPureStruct + } + withBuffer { + var buffer = $0 + if buffer.data.isEmpty { + if isClass(Self._rootAndValueType.root) { + isPureStruct = false + } + } else { + while true { + let (rawComponent, optNextType) = buffer.next() + if isTuple(optNextType as Any) { + isPureStruct = false + _isTuple = true + } + switch rawComponent.header.kind { + case .struct: + _pureStructValueOffset += rawComponent._structOrClassOffset + case .class, .computed, .optionalChain, .optionalForce, .optionalWrap, .external: + isPureStruct = false + } + if optNextType == nil { + break + } + } } + } + return (_pureStructValueOffset, isPureStruct, _isTuple) + } + + internal func _computeOffsetForPureStructKeypath() { + _ = _computeOffsets() + } - if optNextType == nil { return .some(offset) } + // This function was refactored since _computeOffsets() was performing + // essentially the same computation. + @usableFromInline // Exposed as public API by MemoryLayout.offset(of:) + internal var _storedInlineOffset: Int? { + // TODO: Cache this value in a similar manner to _pureStructValueOffset. + // The current design assumes keypath read and write operations will be called + // much more often than MemoryLayout.offset(of:). + let offsetInformation = _computeOffsets() + if offsetInformation.isPureStruct || offsetInformation.isTuple { + return .some(offsetInformation.offset) + } else { + return .none } } } -} + /// A partially type-erased key path, from a concrete root type to any /// resulting value type. @@ -238,6 +303,17 @@ public class KeyPath: PartialKeyPath { @usableFromInline internal final func _projectReadOnly(from root: Root) -> Value { + + //One performance improvement is to skip right to Value + //if this keypath traverses through structs only. + if isPureStructKeyPath + { + return withUnsafeBytes(of: root) { + let pointer = $0.baseAddress.unsafelyUnwrapped.advanced(by: _pureStructValueOffset) + return pointer.assumingMemoryBound(to: Value.self).pointee + } + } + // TODO: For perf, we could use a local growable buffer instead of Any var curBase: Any = root return withBuffer { @@ -294,6 +370,14 @@ public class WritableKeyPath: KeyPath { @usableFromInline internal func _projectMutableAddress(from base: UnsafePointer) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { + + // Don't declare "p" above this if-statement; it may slow things down. + if isPureStructKeyPath + { + let p = UnsafeRawPointer(base).advanced(by: _pureStructValueOffset) + return (pointer: UnsafeMutablePointer( + mutating: p.assumingMemoryBound(to: Value.self)), owner: nil) + } var p = UnsafeRawPointer(base) var type: Any.Type = Root.self var keepAlive: AnyObject? @@ -2240,6 +2324,19 @@ extension _AppendKeyPath /* where Self == ReferenceWritableKeyPath */ { } } +/// Updates information pertaining to the types associated with each key path. +/// +/// Note: Currently we only distinguish between keypaths that traverse only structs to get to the final value, +/// and all other types. This is done for performance reasons. +/// Other type information may be handled in the future to improve performance. +internal func _processAppendingKeyPathType(root: inout AnyKeyPath, leaf: AnyKeyPath) { + root._isPureStructKeyPath = root.isPureStructKeyPath && leaf.isPureStructKeyPath + if let isPureStruct = root._isPureStructKeyPath, isPureStruct { + root._computeOffsetForPureStructKeypath() + } +} + + @usableFromInline internal func _tryToAppendKeyPaths( root: AnyKeyPath, @@ -2265,7 +2362,13 @@ internal func _tryToAppendKeyPaths( } return _openExistential(rootValue, do: open2) } - return _openExistential(rootRoot, do: open) + var returnValue:AnyKeyPath = _openExistential(rootRoot, do: open) + _processAppendingKeyPathType(root: &returnValue, leaf: leaf) + if let returnValue = returnValue as? Result + { + return returnValue + } + return nil } @usableFromInline @@ -2277,7 +2380,7 @@ internal func _appendingKeyPaths< leaf: KeyPath ) -> Result { let resultTy = type(of: root).appendedType(with: type(of: leaf)) - return root.withBuffer { + var returnValue:AnyKeyPath = root.withBuffer { var rootBuffer = $0 return leaf.withBuffer { var leafBuffer = $0 @@ -2411,6 +2514,8 @@ internal func _appendingKeyPaths< return unsafeDowncast(result, to: Result.self) } } + _processAppendingKeyPathType(root: &returnValue, leaf: leaf) + return returnValue as! Result } // The distance in bytes from the address point of a KeyPath object to its From 14436a061b9ba58f04150c5d7932ea31c072a022 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Wed, 24 Aug 2022 16:24:26 -0600 Subject: [PATCH 15/18] Clarified a comment in isTuple(). --- stdlib/public/core/KeyPath.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 5a2140acac988..8dcd7cb89a284 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -180,7 +180,7 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { // TODO: Find a quicker way to see if this is a tuple. internal func isTuple(_ item: Any) -> Bool { // Unwraps type information if possible. - // Otherwise, everything the Mirror sees would be "Optional". + // Otherwise, everything the Mirror handling "item" sees would be "Optional". func unwrapType(_ any: T) -> Any { let mirror = Mirror(reflecting: any) guard mirror.displayStyle == .optional, let first = mirror.children.first else { From ad68085d0e851b9fafe7fadf67b94603bf04e273 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 28 Oct 2022 12:20:47 -0600 Subject: [PATCH 16/18] Added a benchmark for KeyPaths where trivially-typed memory is preceded by non-trivially-typed memory. --- .../KeyPathPerformanceTests.swift | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index f1d5c69c9347f..55067e14f5fbc 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -66,6 +66,12 @@ public let benchmarks = [ tags: [.validation, .api], setUpFunction: setupKeyPathNestedStructs ), + BenchmarkInfo( + name: "KeyPathClassStructs", + runFunction: run_KeyPathClassStructs, + tags: [.validation, .api], + setUpFunction: setupKeyPathNestedStructs + ), ] /** @@ -97,6 +103,7 @@ class FixedSizeArrayHolder { var fixedSizeArray100: FixedSizeArray100 var fixedSizeArray10: FixedSizeArray10 var mainArrayForNestedStructs: [A] + var mainArrayForClassStructs: [D1] var arrayForMutatingGetset: [MutatingGetsetNested1] var arrayForGet: [GetNested1] var arrayForOptionals: [Optional1] @@ -107,6 +114,7 @@ class FixedSizeArrayHolder { var keypathForOptional: KeyPath var keypathForNestedClasses: KeyPath var keypathForNonMutatingGetset: WritableKeyPath + var keypathForClassStructs: WritableKeyPath // Same order as in KeyPathWritePerformance var kp46: WritableKeyPath, ElementType> @@ -176,6 +184,7 @@ class FixedSizeArrayHolder { fixedSizeArray100 = initializeFixedSizeArray100() fixedSizeArray10 = initializeFixedSizeArray10() mainArrayForNestedStructs = [A]() + mainArrayForClassStructs = [D1]() arrayForMutatingGetset = [MutatingGetsetNested1]() arrayForGet = [GetNested1]() arrayForOptionals = [Optional1]() @@ -252,6 +261,7 @@ class FixedSizeArrayHolder { ._nestedItemStorage?!._nestedItemStorage?!._storage) keypathForNestedClasses = identity(\C1.r.r.r.r.a) keypathForNonMutatingGetset = identity(\M.n.o.p.q.q) + keypathForClassStructs = identity(\D1.b.c.d.e.e) } } @@ -262,6 +272,10 @@ public func setupKeyPathNestedStructs() { let instance = A(a: 0, b: B(b: 0, c: C(c: 0, d: D(d: 0, e: E(e: expectedIntForNestedItems))))) holder.mainArrayForNestedStructs.append(instance) + let classStructInstance = D1(b: D2(b: 0, c: D3(c: 0, d: D4(d: 0, + e: D5(e: expectedIntForNestedItems))))) + holder.mainArrayForClassStructs.append(classStructInstance) + var mutatingGetsetInstance = MutatingGetsetNested1() mutatingGetsetInstance.nestedItem.nestedItem.nestedItem.nestedItem .storage = expectedIntForNestedItems @@ -333,6 +347,36 @@ struct E { var e: Int } +// Used for run_KeyPathClassStruct(). +class D1 { + var a: Int + var b: D2 + init(b: D2) + { + a = 0 + self.b = b + } +} + +struct D2 { + var b: Int + var c: D3 +} + +struct D3 { + var c: Int + var d: D4 +} + +struct D4 { + var d: Int + var e: D5 +} + +struct D5 { + var e: Int +} + // Used for run_KeyPathNestedClasses() class C1 { let a: Int = 0 @@ -1326,6 +1370,36 @@ public func run_KeyPathNestedStructs(n: Int) { check(sum == iterationMultipier * n * expectedIntForNestedItems) } +// This measures the performance of keypath reads where a block of +// trivially-typed memory is preceded by something else (optionals, reference +// types, etc.) +@inline(never) +public func run_KeyPathClassStructs(n: Int) { + var sum = 0 + var index = 0 + let iterationMultipier = 2000 + + let singleHopKeyPath0: WritableKeyPath = \D1.b + let singleHopKeyPath1: WritableKeyPath = \D2.c + let singleHopKeyPath2: WritableKeyPath = \D3.d + let singleHopKeyPath3: WritableKeyPath = \D4.e + let singleHopKeyPath4: WritableKeyPath = \D5.e + + let appendedKeyPath = identity(singleHopKeyPath0.appending(path: singleHopKeyPath1) + .appending(path: singleHopKeyPath2).appending(path: singleHopKeyPath3) + .appending(path: singleHopKeyPath4)) + + let elementCount = FixedSizeArrayHolder.shared.mainArrayForClassStructs.count + for _ in 1 ... iterationMultipier * n { + let element = FixedSizeArrayHolder.shared.mainArrayForClassStructs[index] + sum += element[keyPath: appendedKeyPath] + index = (index + 1) % elementCount + } + check(sum == iterationMultipier * n * expectedIntForNestedItems) +} + + + // This is meant as a baseline, from a timing perspective, // for run_testKeyPathReadPerformance() and run_testKeyPathWritePerformance(). // It's currently set to ".skip", but is useful for comparing the performance between keypath operations and direct dot-notation. From c1e03a14152b4cbcf4f967cbc0800d8cbdca75ee Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 4 Nov 2022 13:38:58 -0600 Subject: [PATCH 17/18] Fixing bad rebase on KeyPath.swift. KeyPath.swift should now look like what is currently in main. --- stdlib/public/core/KeyPath.swift | 111 ++++++------------------------- 1 file changed, 20 insertions(+), 91 deletions(-) diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 555b47a364282..9d3ecd54cc24c 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -35,8 +35,6 @@ internal func _abstract( /// type. @_objcRuntimeName(_TtCs11_AnyKeyPath) public class AnyKeyPath: Hashable, _AppendKeyPath { - internal var _isPureStructKeyPath: Bool? - internal var _pureStructValueOffset: Int = 0 /// The root type for this key path. @inlinable public static var rootType: Any.Type { @@ -220,15 +218,15 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { ) -> Self { _internalInvariant(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") - let keypath = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, + let result = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, Int32.self) - keypath._kvcKeyPathStringPtr = nil - let base = UnsafeMutableRawPointer(Builtin.projectTailElems(keypath, + result._kvcKeyPathStringPtr = nil + let base = UnsafeMutableRawPointer(Builtin.projectTailElems(result, Int32.self)) body(UnsafeMutableRawBufferPointer(start: base, count: bytes)) - keypath._computeOffsetForPureStructKeypath() - return keypath + return result } + final internal func withBuffer(_ f: (KeyPathBuffer) throws -> T) rethrows -> T { defer { _fixLifetime(self) } @@ -241,90 +239,26 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { return withBuffer { var buffer = $0 - internal func isClass(_ item: Any.Type) -> Bool { - // Displays "warning: 'is' test is always true" at compile time, but that's not actually the case. - return (item is AnyObject) - } + // The identity key path is effectively a stored keypath of type Self + // at offset zero + if buffer.data.isEmpty { return 0 } - // TODO: Find a quicker way to see if this is a tuple. - internal func isTuple(_ item: Any) -> Bool { - // Unwraps type information if possible. - // Otherwise, everything the Mirror handling "item" sees would be "Optional". - func unwrapType(_ any: T) -> Any { - let mirror = Mirror(reflecting: any) - guard mirror.displayStyle == .optional, let first = mirror.children.first else { - return any - } - return first.value - } + var offset = 0 + while true { + let (rawComponent, optNextType) = buffer.next() + switch rawComponent.header.kind { + case .struct: + offset += rawComponent._structOrClassOffset - let mirror = Mirror(reflecting: unwrapType(item)) - let description = mirror.description - let offsetOfFirstBracketForMirrorDescriptionOfTuple = 11 - let idx = description.index(description.startIndex, offsetBy: offsetOfFirstBracketForMirrorDescriptionOfTuple) - if description[idx] == "(" { - return true - } - return false - } - - // If this keypath traverses structs only, it'll have a predictable memory layout. - // We can then jump to the value directly in _projectReadOnly(). - internal func _computeOffsets() -> (offset: Int, isPureStruct: Bool, isTuple: Bool) { - _pureStructValueOffset = 0 - var isPureStruct = true - var _isTuple = false - defer { - _isPureStructKeyPath = isPureStruct - } - withBuffer { - var buffer = $0 - if buffer.data.isEmpty { - if isClass(Self._rootAndValueType.root) { - isPureStruct = false - } - } else { - while true { - let (rawComponent, optNextType) = buffer.next() - if isTuple(optNextType as Any) { - isPureStruct = false - _isTuple = true - } - switch rawComponent.header.kind { - case .struct: - _pureStructValueOffset += rawComponent._structOrClassOffset - case .class, .computed, .optionalChain, .optionalForce, .optionalWrap, .external: - isPureStruct = false - } - if optNextType == nil { - break - } - } + case .class, .computed, .optionalChain, .optionalForce, .optionalWrap, .external: + return .none } - } - return (_pureStructValueOffset, isPureStruct, _isTuple) - } - internal func _computeOffsetForPureStructKeypath() { - _ = _computeOffsets() - } - - // This function was refactored since _computeOffsets() was performing - // essentially the same computation. - @usableFromInline // Exposed as public API by MemoryLayout.offset(of:) - internal var _storedInlineOffset: Int? { - // TODO: Cache this value in a similar manner to _pureStructValueOffset. - // The current design assumes keypath read and write operations will be called - // much more often than MemoryLayout.offset(of:). - let offsetInformation = _computeOffsets() - if offsetInformation.isPureStruct || offsetInformation.isTuple { - return .some(offsetInformation.offset) - } else { - return .none + if optNextType == nil { return .some(offset) } } } } - +} /// A partially type-erased key path, from a concrete root type to any /// resulting value type. @@ -446,6 +380,7 @@ public class WritableKeyPath: KeyPath { @usableFromInline internal func _projectMutableAddress(from base: UnsafePointer) -> (pointer: UnsafeMutablePointer, owner: AnyObject?) { + // One performance improvement is to skip right to Value // if this keypath traverses through structs only. @@ -2452,13 +2387,7 @@ internal func _tryToAppendKeyPaths( } return _openExistential(rootValue, do: open2) } - var returnValue:AnyKeyPath = _openExistential(rootRoot, do: open) - _processAppendingKeyPathType(root: &returnValue, leaf: leaf) - if let returnValue = returnValue as? Result - { - return returnValue - } - return nil + return _openExistential(rootRoot, do: open) } @usableFromInline From f526e08d68ff51e0c3539962d7da21ec04a1b977 Mon Sep 17 00:00:00 2001 From: Martin Cwikla Date: Fri, 4 Nov 2022 15:40:33 -0600 Subject: [PATCH 18/18] Reduces the workload of run_KeyPathClassStructs by a factor of 4. The reported time was 1847 us. --- benchmark/single-source/KeyPathPerformanceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/single-source/KeyPathPerformanceTests.swift b/benchmark/single-source/KeyPathPerformanceTests.swift index 55067e14f5fbc..98fa4f76d20e6 100644 --- a/benchmark/single-source/KeyPathPerformanceTests.swift +++ b/benchmark/single-source/KeyPathPerformanceTests.swift @@ -1377,7 +1377,7 @@ public func run_KeyPathNestedStructs(n: Int) { public func run_KeyPathClassStructs(n: Int) { var sum = 0 var index = 0 - let iterationMultipier = 2000 + let iterationMultipier = 500 let singleHopKeyPath0: WritableKeyPath = \D1.b let singleHopKeyPath1: WritableKeyPath = \D2.c