Skip to content

Commit e0890e9

Browse files
authored
Merge pull request #17763 from lorentey/nsdictionary-speedup-4.2
[4.2][stdlib] Speed up bridged Dictionary instances
2 parents 8164fa2 + f9b03d9 commit e0890e9

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/DictionaryLiteral
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
@@ -52,6 +52,7 @@ import DictTest3
5252
import DictTest4
5353
import DictTest4Legacy
5454
import DictionaryBridge
55+
import DictionaryBridgeToObjC
5556
import DictionaryCopy
5657
import DictionaryGroup
5758
import DictionaryLiteral
@@ -208,6 +209,7 @@ registerBenchmark(Dictionary3)
208209
registerBenchmark(Dictionary4)
209210
registerBenchmark(Dictionary4Legacy)
210211
registerBenchmark(DictionaryBridge)
212+
registerBenchmark(DictionaryBridgeToObjC)
211213
registerBenchmark(DictionaryCopy)
212214
registerBenchmark(DictionaryGroup)
213215
registerBenchmark(DictionaryLiteral)

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) LLVM_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
@@ -1623,7 +1623,7 @@ internal func _stdlib_NSDictionary_allKeys(_ nsd: _NSDictionary)
16231623
let storage = _HeapBuffer<Int, AnyObject>(
16241624
_HeapBufferStorage<Int, AnyObject>.self, count, count)
16251625

1626-
nsd.getObjects(nil, andKeys: storage.baseAddress)
1626+
nsd.getObjects(nil, andKeys: storage.baseAddress, count: count)
16271627
return storage
16281628
}
16291629
#endif
@@ -1999,8 +1999,11 @@ internal class _RawNativeDictionaryStorage
19991999
}
20002000

20012001
@inlinable // FIXME(sil-serialize-all)
2002-
internal func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
2003-
andKeys keys: UnsafeMutablePointer<AnyObject>?) {
2002+
@objc(getObjects:andKeys:count:)
2003+
internal func getObjects(
2004+
_ objects: UnsafeMutablePointer<AnyObject>?,
2005+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
2006+
count: Int) {
20042007
// Do nothing, we're empty
20052008
}
20062009
#endif
@@ -2168,28 +2171,38 @@ final internal class _HashableTypedNativeDictionaryStorage<Key: Hashable, Value>
21682171

21692172
// We also override the following methods for efficiency.
21702173
@inlinable // FIXME(sil-serialize-all)
2171-
@objc
2172-
override func getObjects(_ objects: UnsafeMutablePointer<AnyObject>?,
2173-
andKeys keys: UnsafeMutablePointer<AnyObject>?) {
2174-
// The user is expected to provide a storage of the correct size
2174+
@objc(getObjects:andKeys:count:)
2175+
override func getObjects(
2176+
_ objects: UnsafeMutablePointer<AnyObject>?,
2177+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
2178+
count: Int) {
2179+
_precondition(count >= 0, "Invalid count")
2180+
guard count > 0 else { return }
2181+
var i = 0 // Current position in the output buffers
21752182
if let unmanagedKeys = _UnmanagedAnyObjectArray(keys) {
21762183
if let unmanagedObjects = _UnmanagedAnyObjectArray(objects) {
21772184
// keys nonnull, objects nonnull
2178-
for (offset: i, element: (key: key, value: val)) in full.enumerated() {
2179-
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(val)
2185+
for (key, value) in full {
2186+
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
21802187
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
2188+
i += 1
2189+
guard i < count else { break }
21812190
}
21822191
} else {
21832192
// keys nonnull, objects null
2184-
for (offset: i, element: (key: key, value: _)) in full.enumerated() {
2193+
for (key, _) in full {
21852194
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
2195+
i += 1
2196+
guard i < count else { break }
21862197
}
21872198
}
21882199
} else {
21892200
if let unmanagedObjects = _UnmanagedAnyObjectArray(objects) {
21902201
// keys null, objects nonnull
2191-
for (offset: i, element: (key: _, value: val)) in full.enumerated() {
2192-
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(val)
2202+
for (_, value) in full {
2203+
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
2204+
i += 1
2205+
guard i < count else { break }
21932206
}
21942207
} else {
21952208
// do nothing, both are null
@@ -2862,12 +2875,13 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
28622875
}
28632876

28642877
@inlinable // FIXME(sil-serialize-all)
2865-
@objc
2878+
@objc(getObjects:andKeys:count:)
28662879
internal func getObjects(
28672880
_ objects: UnsafeMutablePointer<AnyObject>?,
2868-
andKeys keys: UnsafeMutablePointer<AnyObject>?
2881+
andKeys keys: UnsafeMutablePointer<AnyObject>?,
2882+
count: Int
28692883
) {
2870-
bridgedAllKeysAndValues(objects, keys)
2884+
bridgedAllKeysAndValues(objects, keys, count)
28712885
}
28722886

28732887
@inlinable // FIXME(sil-serialize-all)
@@ -2957,11 +2971,13 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29572971
@nonobjc
29582972
internal func bridgedAllKeysAndValues(
29592973
_ objects: UnsafeMutablePointer<AnyObject>?,
2960-
_ keys: UnsafeMutablePointer<AnyObject>?
2974+
_ keys: UnsafeMutablePointer<AnyObject>?,
2975+
_ count: Int
29612976
) {
2977+
_precondition(count >= 0, "Invalid count")
2978+
guard count > 0 else { return }
29622979
bridgeEverything()
2963-
// The user is expected to provide a storage of the correct size
2964-
var i = 0 // Position in the input storage
2980+
var i = 0 // Current position in the output buffers
29652981
let bucketCount = nativeBuffer.bucketCount
29662982

29672983
if let unmanagedKeys = _UnmanagedAnyObjectArray(keys) {
@@ -2972,6 +2988,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29722988
unmanagedObjects[i] = bridgedBuffer.value(at: position)
29732989
unmanagedKeys[i] = bridgedBuffer.key(at: position)
29742990
i += 1
2991+
guard i < count else { break }
29752992
}
29762993
}
29772994
} else {
@@ -2980,6 +2997,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29802997
if bridgedBuffer.isInitializedEntry(at: position) {
29812998
unmanagedKeys[i] = bridgedBuffer.key(at: position)
29822999
i += 1
3000+
guard i < count else { break }
29833001
}
29843002
}
29853003
}
@@ -2990,6 +3008,7 @@ final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
29903008
if bridgedBuffer.isInitializedEntry(at: position) {
29913009
unmanagedObjects[i] = bridgedBuffer.value(at: position)
29923010
i += 1
3011+
guard i < count else { break }
29933012
}
29943013
}
29953014
} 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)