Skip to content

Commit 5e642d2

Browse files
committed
[stdlib] Optimize Set.isSubset<S>(of: S)
Use a temporary bitset to speed up the Sequence variant by roughly a factor of 3.
1 parent 6b1f6c0 commit 5e642d2

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

stdlib/public/core/NativeSet.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ extension _NativeSet { // Primitive fields
7777
}
7878
}
7979

80+
@_alwaysEmitIntoClient
81+
@inline(__always)
82+
internal var bucketCount: Int {
83+
_assumeNonNegative(_storage._bucketCount)
84+
}
85+
8086
@inlinable
8187
internal var hashTable: _HashTable {
8288
@inline(__always) get {
@@ -580,3 +586,26 @@ extension _NativeSet.Iterator: IteratorProtocol {
580586
return base.uncheckedElement(at: index)
581587
}
582588
}
589+
590+
extension _NativeSet {
591+
@_alwaysEmitIntoClient
592+
internal func isSubset<S: Sequence>(of possibleSuperset: S) -> Bool
593+
where S.Element == Element {
594+
_UnsafeBitset.withTemporaryBitset(capacity: self.bucketCount) { seen in
595+
// Mark elements in self that we've seen in `possibleSuperset`.
596+
var seenCount = 0
597+
for element in possibleSuperset {
598+
let (bucket, found) = find(element)
599+
guard found else { continue }
600+
let inserted = seen.uncheckedInsert(bucket.offset)
601+
if inserted {
602+
seenCount += 1
603+
if seenCount == self.count {
604+
return true
605+
}
606+
}
607+
}
608+
return false
609+
}
610+
}
611+
}

stdlib/public/core/Set.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -710,9 +710,11 @@ extension Set: SetAlgebra {
710710
public func isSubset<S: Sequence>(of possibleSuperset: S) -> Bool
711711
where S.Element == Element {
712712
guard !isEmpty else { return true }
713-
714-
let other = Set(possibleSuperset)
715-
return isSubset(of: other)
713+
if self.count == 1 { return possibleSuperset.contains(self.first!) }
714+
if let s = possibleSuperset as? Set<Element> {
715+
return isSubset(of: s)
716+
}
717+
return _variant.convertedToNative.isSubset(of: possibleSuperset)
716718
}
717719

718720
/// Returns a Boolean value that indicates whether the set is a strict subset
@@ -1081,7 +1083,7 @@ extension Set {
10811083
public func isSubset(of other: Set<Element>) -> Bool {
10821084
guard self.count <= other.count else { return false }
10831085
for member in self {
1084-
if !other.contains(member) {
1086+
guard other.contains(member) else {
10851087
return false
10861088
}
10871089
}

0 commit comments

Comments
 (0)