Skip to content

Commit 003ec25

Browse files
authored
Merge pull request #17742 from lorentey/nsdictionary-speedup
2 parents 86ba697 + cf7e438 commit 003ec25

File tree

8 files changed

+250
-39
lines changed

8 files changed

+250
-39
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ set(SWIFT_BENCH_MODULES
6565
single-source/DictTest4
6666
single-source/DictTest4Legacy
6767
single-source/DictionaryBridge
68+
single-source/DictionaryBridgeToObjC
6869
single-source/DictionaryCopy
6970
single-source/DictionaryGroup
7071
single-source/DictionaryKeysContains
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- DictionaryBridgeToObjC.swift -------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// Performance benchmark for common operations on Dictionary values bridged to
14+
// NSDictionary.
15+
import TestsUtils
16+
#if _runtime(_ObjC)
17+
import Foundation
18+
19+
public let DictionaryBridgeToObjC = [
20+
BenchmarkInfo(
21+
name: "DictionaryBridgeToObjC_Bridge",
22+
runFunction: run_DictionaryBridgeToObjC_BridgeToObjC,
23+
tags: [.validation, .api, .Dictionary, .bridging]),
24+
BenchmarkInfo(
25+
name: "DictionaryBridgeToObjC_Access",
26+
runFunction: run_DictionaryBridgeToObjC_Access,
27+
tags: [.validation, .api, .Dictionary, .bridging]),
28+
BenchmarkInfo(
29+
name: "DictionaryBridgeToObjC_BulkAccess",
30+
runFunction: run_DictionaryBridgeToObjC_BulkAccess,
31+
tags: [.validation, .api, .Dictionary, .bridging])
32+
]
33+
34+
let numbers: [String: Int] = [
35+
"one": 1,
36+
"two": 2,
37+
"three": 3,
38+
"four": 4,
39+
"five": 5,
40+
"six": 6,
41+
"seven": 7,
42+
"eight": 8,
43+
"nine": 9,
44+
"ten": 10,
45+
"eleven": 11,
46+
"twelve": 12,
47+
"thirteen": 13,
48+
"fourteen": 14,
49+
"fifteen": 15,
50+
"sixteen": 16,
51+
"seventeen": 17,
52+
"eighteen": 18,
53+
"nineteen": 19,
54+
"twenty": 20
55+
]
56+
57+
@inline(never)
58+
public func run_DictionaryBridgeToObjC_BridgeToObjC(_ N: Int) {
59+
for _ in 1 ... 100 * N {
60+
blackHole(numbers as NSDictionary)
61+
}
62+
}
63+
64+
@inline(never)
65+
public func run_DictionaryBridgeToObjC_Access(_ N: Int) {
66+
let d = numbers as NSDictionary
67+
blackHole(d.object(forKey: "one")) // Force bridging of contents
68+
for _ in 1 ... 100 * N {
69+
for key in numbers.keys {
70+
CheckResults(identity(d).object(forKey: key) != nil)
71+
}
72+
}
73+
}
74+
75+
@inline(never)
76+
public func run_DictionaryBridgeToObjC_BulkAccess(_ N: Int) {
77+
let d = numbers as NSDictionary
78+
for _ in 1 ... 100 * N {
79+
let d2 = NSDictionary(dictionary: identity(d))
80+
CheckResults(d2.count == d.count)
81+
}
82+
}
83+
84+
#else // !_runtime(ObjC)
85+
public let DictionaryBridgeToObjC: [BenchmarkInfo] = []
86+
#endif

benchmark/utils/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import DictTest3
5353
import DictTest4
5454
import DictTest4Legacy
5555
import DictionaryBridge
56+
import DictionaryBridgeToObjC
5657
import DictionaryCopy
5758
import DictionaryGroup
5859
import DictionaryKeysContains
@@ -215,6 +216,7 @@ registerBenchmark(Dictionary3)
215216
registerBenchmark(Dictionary4)
216217
registerBenchmark(Dictionary4Legacy)
217218
registerBenchmark(DictionaryBridge)
219+
registerBenchmark(DictionaryBridgeToObjC)
218220
registerBenchmark(DictionaryCopy)
219221
registerBenchmark(DictionaryGroup)
220222
registerBenchmark(DictionaryKeysContains)

stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public func withOverriddenLocaleCurrentLocale<Result>(
7272
public func autoreleasepoolIfUnoptimizedReturnAutoreleased(
7373
invoking body: () -> Void
7474
) {
75-
#if arch(i386) && (os(iOS) || os(watchOS))
75+
#if targetEnvironment(simulator) && arch(i386) && (os(iOS) || os(watchOS))
7676
autoreleasepool(invoking: body)
7777
#else
7878
body()
@@ -100,23 +100,26 @@ extension NSArray {
100100
}
101101
}
102102

103-
@_silgen_name("NSDictionary_getObjects")
104-
func NSDictionary_getObjects(
103+
@_silgen_name("NSDictionary_getObjectsAndKeysWithCount")
104+
func NSDictionary_getObjectsAndKeysWithCount(
105105
nsDictionary: NSDictionary,
106106
objects: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
107-
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?
107+
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
108+
count: Int
108109
)
109110

110111
extension NSDictionary {
111112
@nonobjc // FIXME: there should be no need in this attribute.
112113
public func available_getObjects(
113114
_ objects: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
114-
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?
115+
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
116+
count: Int
115117
) {
116-
return NSDictionary_getObjects(
118+
return NSDictionary_getObjectsAndKeysWithCount(
117119
nsDictionary: self,
118120
objects: objects,
119-
andKeys: keys)
121+
andKeys: keys,
122+
count: count)
120123
}
121124
}
122125

stdlib/private/StdlibUnittestFoundationExtras/UnavailableFoundationMethodThunks.mm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424

2525
SWIFT_CC(swift) SWIFT_RUNTIME_LIBRARY_VISIBILITY
2626
extern "C" void
27-
NSDictionary_getObjects(NSDictionary *_Nonnull nsDictionary,
28-
id *objects, id *keys) {
29-
[nsDictionary getObjects:objects andKeys:keys];
27+
NSDictionary_getObjectsAndKeysWithCount(NSDictionary *_Nonnull nsDictionary,
28+
id *objects, id *keys,
29+
NSInteger count) {
30+
[nsDictionary getObjects:objects andKeys:keys count:count];
3031
SWIFT_CC_PLUSONE_GUARD([nsDictionary release]);
3132
}
3233

stdlib/public/core/Dictionary.swift

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,7 @@ internal func _stdlib_NSDictionary_allKeys(_ nsd: _NSDictionary)
15821582
let storage = _HeapBuffer<Int, AnyObject>(
15831583
_HeapBufferStorage<Int, AnyObject>.self, count, count)
15841584

1585-
nsd.getObjects(nil, andKeys: storage.baseAddress)
1585+
nsd.getObjects(nil, andKeys: storage.baseAddress, count: count)
15861586
return storage
15871587
}
15881588
#endif
@@ -1959,8 +1959,11 @@ internal class _RawNativeDictionaryStorage
19591959
}
19601960

19611961
@inlinable // FIXME(sil-serialize-all)
1962-
internal func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
1963-
andKeys keys: UnsafeMutablePointer<AnyObject>?) {
1962+
@objc(getObjects:andKeys:count:)
1963+
internal func getObjects(
1964+
_ objects: UnsafeMutablePointer<AnyObject>?,
1965+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
1966+
count: Int) {
19641967
// Do nothing, we're empty
19651968
}
19661969
#endif
@@ -2130,28 +2133,38 @@ final internal class _HashableTypedNativeDictionaryStorage<Key: Hashable, Value>
21302133

21312134
// We also override the following methods for efficiency.
21322135
@inlinable // FIXME(sil-serialize-all)
2133-
@objc
2134-
override func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
2135-
andKeys keys: UnsafeMutablePointer<AnyObject>?) {
2136-
// The user is expected to provide a storage of the correct size
2136+
@objc(getObjects:andKeys:count:)
2137+
override func getObjects(
2138+
_ objects: UnsafeMutablePointer<AnyObject>?,
2139+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
2140+
count: Int) {
2141+
_precondition(count >= 0, "Invalid count")
2142+
guard count > 0 else { return }
2143+
var i = 0 // Current position in the output buffers
21372144
if let unmanagedKeys = _UnmanagedAnyObjectArray(keys) {
21382145
if let unmanagedObjects = _UnmanagedAnyObjectArray(objects) {
21392146
// keys nonnull, objects nonnull
2140-
for (offset: i, element: (key: key, value: val)) in full.enumerated() {
2141-
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(val)
2147+
for (key, value) in full {
2148+
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
21422149
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
2150+
i += 1
2151+
guard i < count else { break }
21432152
}
21442153
} else {
21452154
// keys nonnull, objects null
2146-
for (offset: i, element: (key: key, value: _)) in full.enumerated() {
2155+
for (key, _) in full {
21472156
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
2157+
i += 1
2158+
guard i < count else { break }
21482159
}
21492160
}
21502161
} else {
21512162
if let unmanagedObjects = _UnmanagedAnyObjectArray(objects) {
21522163
// keys null, objects nonnull
2153-
for (offset: i, element: (key: _, value: val)) in full.enumerated() {
2154-
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(val)
2164+
for (_, value) in full {
2165+
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
2166+
i += 1
2167+
guard i < count else { break }
21552168
}
21562169
} else {
21572170
// do nothing, both are null
@@ -2835,12 +2848,13 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
28352848
}
28362849

28372850
@inlinable // FIXME(sil-serialize-all)
2838-
@objc
2851+
@objc(getObjects:andKeys:count:)
28392852
internal func getObjects(
28402853
_ objects: UnsafeMutablePointer<AnyObject>?,
2841-
andKeys keys: UnsafeMutablePointer<AnyObject>?
2854+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
2855+
count: Int
28422856
) {
2843-
bridgedAllKeysAndValues(objects, keys)
2857+
bridgedAllKeysAndValues(objects, keys, count)
28442858
}
28452859

28462860
@inlinable // FIXME(sil-serialize-all)
@@ -2930,11 +2944,13 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29302944
@nonobjc
29312945
internal func bridgedAllKeysAndValues(
29322946
_ objects: UnsafeMutablePointer<AnyObject>?,
2933-
_ keys: UnsafeMutablePointer<AnyObject>?
2947+
_ keys: UnsafeMutablePointer<AnyObject>?,
2948+
_ count: Int
29342949
) {
2950+
_precondition(count >= 0, "Invalid count")
2951+
guard count > 0 else { return }
29352952
bridgeEverything()
2936-
// The user is expected to provide a storage of the correct size
2937-
var i = 0 // Position in the input storage
2953+
var i = 0 // Current position in the output buffers
29382954
let bucketCount = nativeBuffer.bucketCount
29392955

29402956
if let unmanagedKeys = _UnmanagedAnyObjectArray(keys) {
@@ -2945,6 +2961,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29452961
unmanagedObjects[i] = bridgedBuffer.value(at: position)
29462962
unmanagedKeys[i] = bridgedBuffer.key(at: position)
29472963
i += 1
2964+
guard i < count else { break }
29482965
}
29492966
}
29502967
} else {
@@ -2953,6 +2970,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29532970
if bridgedBuffer.isInitializedEntry(at: position) {
29542971
unmanagedKeys[i] = bridgedBuffer.key(at: position)
29552972
i += 1
2973+
guard i < count else { break }
29562974
}
29572975
}
29582976
}
@@ -2963,6 +2981,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29632981
if bridgedBuffer.isInitializedEntry(at: position) {
29642982
unmanagedObjects[i] = bridgedBuffer.value(at: position)
29652983
i += 1
2984+
guard i < count else { break }
29662985
}
29672986
}
29682987
} else {

stdlib/public/core/ShadowProtocols.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,12 @@ public protocol _NSDictionaryCore :
103103
@objc(copyWithZone:)
104104
func copy(with zone: _SwiftNSZone?) -> AnyObject
105105

106-
func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
107-
andKeys keys: UnsafeMutablePointer<AnyObject>?)
106+
@objc(getObjects:andKeys:count:)
107+
func getObjects(
108+
_ objects: UnsafeMutablePointer<AnyObject>?,
109+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
110+
count: Int
111+
)
108112

109113
@objc(countByEnumeratingWithState:objects:count:)
110114
func countByEnumerating(
@@ -125,9 +129,11 @@ public protocol _NSDictionaryCore :
125129
public protocol _NSDictionary : _NSDictionaryCore {
126130
// Note! This API's type is different from what is imported by the clang
127131
// importer.
128-
func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
129-
andKeys keys: UnsafeMutablePointer<AnyObject>?)
130-
}
132+
func getObjects(
133+
_ objects: UnsafeMutablePointer<AnyObject>?,
134+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
135+
count: Int)
136+
}
131137

132138
/// A shadow for the "core operations" of NSSet.
133139
///

0 commit comments

Comments
 (0)