Skip to content

Commit db80c78

Browse files
committed
[stdlib] Optimize Set.filter(_:)
This works the same way as `Set.subtracting<S>(_:)`, and has similar performance benefits.
1 parent 80296bb commit db80c78

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
lines changed

stdlib/public/core/HashTable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal struct _HashTable {
4242
@inlinable
4343
internal var bucketCount: Int {
4444
@inline(__always) get {
45-
return bucketMask &+ 1
45+
return _assumeNonNegative(bucketMask &+ 1)
4646
}
4747
}
4848

stdlib/public/core/NativeSet.swift

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,25 @@ extension _NativeSet {
658658
}
659659
}
660660

661+
@_alwaysEmitIntoClient
662+
internal __consuming func extractSubset(
663+
using bitset: _UnsafeBitset,
664+
count: Int
665+
) -> _NativeSet {
666+
var count = count
667+
if count == 0 { return _NativeSet() }
668+
if count == self.count { return self }
669+
let result = _NativeSet(capacity: count)
670+
for offset in bitset {
671+
result._unsafeInsertNew(self.uncheckedElement(at: Bucket(offset: offset)))
672+
// The hash table can have set bits after the end of the bitmap.
673+
// Ignore them.
674+
count -= 1
675+
if count == 0 { break }
676+
}
677+
return result
678+
}
679+
661680
@_alwaysEmitIntoClient
662681
internal __consuming func subtracting<S: Sequence>(_ other: S) -> _NativeSet
663682
where S.Element == Element {
@@ -677,18 +696,24 @@ extension _NativeSet {
677696
}
678697
}
679698
}
680-
_internalInvariant(remainingCount > 0)
681-
if remainingCount == self.count { return self }
682-
let result = _NativeSet(capacity: remainingCount)
683-
for offset in difference {
684-
result._unsafeInsertNew(
685-
self.uncheckedElement(at: Bucket(offset: offset)))
686-
// The hash table can have set bits after the end of the bitmap.
687-
// Ignore them.
688-
remainingCount -= 1
689-
if remainingCount == 0 { break }
699+
_internalInvariant(difference.count > 0)
700+
return extractSubset(using: difference, count: remainingCount)
701+
}
702+
}
703+
704+
@_alwaysEmitIntoClient
705+
internal __consuming func filter(
706+
_ isIncluded: (Element) throws -> Bool
707+
) rethrows -> _NativeSet<Element> {
708+
try _UnsafeBitset.withTemporaryBitset(capacity: bucketCount) { bitset in
709+
var count = 0
710+
for bucket in hashTable {
711+
if try isIncluded(uncheckedElement(at: bucket)) {
712+
bitset.uncheckedInsert(bucket.offset)
713+
count += 1
714+
}
690715
}
691-
return result
716+
return extractSubset(using: bitset, count: count)
692717
}
693718
}
694719
}

stdlib/public/core/Set.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,14 +295,7 @@ extension Set {
295295
public __consuming func filter(
296296
_ isIncluded: (Element) throws -> Bool
297297
) rethrows -> Set {
298-
// FIXME(performance): Eliminate rehashes by using a bitmap.
299-
var result = Set()
300-
for element in self {
301-
if try isIncluded(element) {
302-
result.insert(element)
303-
}
304-
}
305-
return result
298+
return try Set(_native: _variant.filter(isIncluded))
306299
}
307300
}
308301

stdlib/public/core/SetVariant.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,24 @@ extension Set._Variant {
376376
}
377377
}
378378

379+
extension Set._Variant {
380+
@_alwaysEmitIntoClient
381+
internal __consuming func filter(
382+
_ isIncluded: (Element) throws -> Bool
383+
) rethrows -> _NativeSet<Element> {
384+
#if _runtime(_ObjC)
385+
guard isNative else {
386+
var result = _NativeSet<Element>()
387+
for cocoaElement in asCocoa {
388+
let nativeElement = _forceBridgeFromObjectiveC(
389+
cocoaElement, Element.self)
390+
if try isIncluded(nativeElement) {
391+
result.insertNew(nativeElement, isUnique: true)
392+
}
393+
}
394+
return result
395+
}
396+
#endif
397+
return try asNative.filter(isIncluded)
398+
}
399+
}

0 commit comments

Comments
 (0)