Skip to content

Commit 2612169

Browse files
committed
Optimize Dictionary.filter for native dictionaries
1 parent 3ee8034 commit 2612169

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

stdlib/public/core/Dictionary.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -608,15 +608,17 @@ extension Dictionary {
608608
public __consuming func filter(
609609
_ isIncluded: (Element) throws -> Bool
610610
) rethrows -> [Key: Value] {
611-
// FIXME(performance): Try building a bitset of elements to keep, so that we
612-
// eliminate rehashings during insertion.
613-
var result = _NativeDictionary<Key, Value>()
614-
for element in self {
615-
if try isIncluded(element) {
616-
result.insertNew(key: element.key, value: element.value)
611+
if _variant.isNative {
612+
return Dictionary(_native: try _variant.asNative.filter(isIncluded))
613+
} else {
614+
var result = _NativeDictionary<Key, Value>()
615+
for element in self {
616+
if try isIncluded(element) {
617+
result.insertNew(key: element.key, value: element.value)
618+
}
617619
}
620+
return Dictionary(_native: result)
618621
}
619-
return Dictionary(_native: result)
620622
}
621623
}
622624

stdlib/public/core/NativeDictionary.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,27 @@ extension _NativeDictionary {
599599
(_values + a.offset).moveInitialize(from: _values + b.offset, count: 1)
600600
(_values + b.offset).initialize(to: value)
601601
}
602+
603+
@inlinable
604+
internal func extractDictionary(
605+
using bitset: _UnsafeBitset,
606+
count: Int
607+
) -> _NativeDictionary<Key, Value> {
608+
var count = count
609+
if count == 0 { return _NativeDictionary<Key, Value>() }
610+
if count == self.count { return self }
611+
let result = _NativeDictionary<Key, Value>(capacity: count)
612+
for offset in bitset {
613+
let key = self.uncheckedKey(at: Bucket(offset: offset))
614+
let value = self.uncheckedValue(at: Bucket(offset: offset))
615+
result._unsafeInsertNew(key: key, value: value)
616+
// The hash table can have set bits after the end of the bitmap.
617+
// Ignore them.
618+
count -= 1
619+
if count == 0 { break }
620+
}
621+
return result
622+
}
602623
}
603624

604625
extension _NativeDictionary where Value: Equatable {
@@ -771,6 +792,26 @@ extension _NativeDictionary { // High-level operations
771792
}
772793
}
773794
}
795+
796+
@inlinable
797+
internal func filter(
798+
_ isIncluded: (Element) throws -> Bool
799+
) rethrows -> _NativeDictionary<Key, Value> {
800+
try _UnsafeBitset.withTemporaryBitset(
801+
capacity: _storage._bucketCount
802+
) { bitset in
803+
var count = 0
804+
for bucket in hashTable {
805+
if try isIncluded(
806+
(uncheckedKey(at: bucket), uncheckedValue(at: bucket))
807+
) {
808+
bitset.uncheckedInsert(bucket.offset)
809+
count += 1
810+
}
811+
}
812+
return extractDictionary(using: bitset, count: count)
813+
}
814+
}
774815
}
775816

776817
extension _NativeDictionary: Sequence {

0 commit comments

Comments
 (0)