Skip to content

Commit 31b7e0e

Browse files
natecook1000airspeedswift
authored andcommitted
[4.0] Move Dictionary(grouping:by) down a level (#10450)
* [benchmark] Add benchmark for Dictionary(group:by:) * [benchmark] Fix the Dictionary(group:by:) test When testing quadratic behavior, remember to repeat small amounts of the behavior several times, not to increase the N, which is squared. :roll_eyes: * [stdlib] Move Dictionary(grouping:by) down a level
1 parent aa9e077 commit 31b7e0e

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ set(SWIFT_BENCH_MODULES
4040
single-source/DictTest2
4141
single-source/DictTest3
4242
single-source/DictionaryBridge
43+
single-source/DictionaryGroup
4344
single-source/DictionaryLiteral
4445
single-source/DictionaryRemove
4546
single-source/DictionarySwap
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===--- DictionaryGroup.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
import TestsUtils
14+
15+
let count = 10_000
16+
let result = count / 10
17+
18+
@inline(never)
19+
public func run_DictionaryGroup(_ N: Int) {
20+
for _ in 1...N {
21+
let dict = Dictionary(grouping: 0..<count, by: { $0 % 10 })
22+
CheckResults(dict.count == 10)
23+
CheckResults(dict[0]!.count == result)
24+
}
25+
}
26+
27+
class Box<T : Hashable> : Hashable {
28+
var value: T
29+
30+
init(_ v: T) {
31+
value = v
32+
}
33+
34+
var hashValue: Int {
35+
return value.hashValue
36+
}
37+
38+
static func ==(lhs: Box, rhs: Box) -> Bool {
39+
return lhs.value == rhs.value
40+
}
41+
}
42+
43+
@inline(never)
44+
public func run_DictionaryGroupOfObjects(_ N: Int) {
45+
let objects = (0..<count).map { Box($0) }
46+
for _ in 1...N {
47+
let dict = Dictionary(grouping: objects, by: { Box($0.value % 10) })
48+
CheckResults(dict.count == 10)
49+
CheckResults(dict[Box(0)]!.count == result)
50+
}
51+
}

benchmark/utils/main.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import DictTest
4545
import DictTest2
4646
import DictTest3
4747
import DictionaryBridge
48+
import DictionaryGroup
4849
import DictionaryLiteral
4950
import DictionaryRemove
5051
import DictionarySwap
@@ -181,6 +182,8 @@ addTo(&precommitTests, "Dictionary2OfObjects", run_Dictionary2OfObjects)
181182
addTo(&precommitTests, "Dictionary3", run_Dictionary3)
182183
addTo(&precommitTests, "Dictionary3OfObjects", run_Dictionary3OfObjects)
183184
addTo(&precommitTests, "DictionaryBridge", run_DictionaryBridge)
185+
addTo(&precommitTests, "DictionaryGroup", run_DictionaryGroup)
186+
addTo(&precommitTests, "DictionaryGroupOfObjects", run_DictionaryGroupOfObjects)
184187
addTo(&precommitTests, "DictionaryLiteral", run_DictionaryLiteral)
185188
addTo(&precommitTests, "DictionaryOfObjects", run_DictionaryOfObjects)
186189
addTo(&precommitTests, "DictionaryRemove", run_DictionaryRemove)

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,9 +1810,7 @@ public struct Dictionary<Key : Hashable, Value> :
18101810
by keyForValue: (S.Element) throws -> Key
18111811
) rethrows where Value == [S.Element] {
18121812
self = [:]
1813-
for value in values {
1814-
self[try keyForValue(value), default: []].append(value)
1815-
}
1813+
try _variantBuffer.nativeGroup(values, by: keyForValue)
18161814
}
18171815

18181816
internal init(_nativeBuffer: _NativeDictionaryBuffer<Key, Value>) {
@@ -5053,6 +5051,32 @@ internal enum _Variant${Self}Buffer<${TypeParametersDecl}> : _HashBuffer {
50535051
#endif
50545052
}
50555053
}
5054+
5055+
internal mutating func nativeGroup<S: Sequence>(
5056+
_ values: S,
5057+
by keyForValue: (S.Element) throws -> Key
5058+
) rethrows where Value == [S.Element] {
5059+
defer { _fixLifetime(asNative) }
5060+
for value in values {
5061+
let key = try keyForValue(value)
5062+
var (i, found) = asNative._find(key, startBucket: asNative._bucket(key))
5063+
if found {
5064+
asNative.values[i.offset].append(value)
5065+
} else {
5066+
let minCapacity = NativeBuffer.minimumCapacity(
5067+
minimumCount: asNative.count + 1,
5068+
maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
5069+
5070+
let (_, capacityChanged) = ensureUniqueNativeBuffer(minCapacity)
5071+
if capacityChanged {
5072+
i = asNative._find(key, startBucket: asNative._bucket(key)).pos
5073+
}
5074+
5075+
asNative.initializeKey(key, value: [value], at: i.offset)
5076+
asNative.count += 1
5077+
}
5078+
}
5079+
}
50565080
%end
50575081

50585082
/// - parameter idealBucket: The ideal bucket for the element being deleted.

0 commit comments

Comments
 (0)