Skip to content

Commit e5c1567

Browse files
authored
[stdlib] Switch to a stable sort algorithm (swiftlang#19717)
This switches the standard library's sort algorithm from an in-place introsort to use a modified timsort, a stable, adaptive sort that merges runs using a temporary buffer. This implementation performs straight merges instead of adopting timsort's galloping strategy. In addition to maintaining the relative order of equal/non-comparable elements, this algorithm outperforms the introsort on data with any intrinsic structure, such as runs of ascending or descending elements or a significant number of equality collisions.
1 parent 50e5a66 commit e5c1567

File tree

8 files changed

+940
-387
lines changed

8 files changed

+940
-387
lines changed

stdlib/public/core/CollectionAlgorithms.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,14 +320,21 @@ extension MutableCollection where Self : BidirectionalCollection {
320320
) rethrows -> Index {
321321
let maybeOffset = try _withUnsafeMutableBufferPointerIfSupported {
322322
(bufferPointer) -> Int in
323-
let unsafeBufferPivot = try bufferPointer.partition(
323+
let unsafeBufferPivot = try bufferPointer._partitionImpl(
324324
by: belongsInSecondPartition)
325325
return unsafeBufferPivot - bufferPointer.startIndex
326326
}
327327
if let offset = maybeOffset {
328-
return index(startIndex, offsetBy: numericCast(offset))
328+
return index(startIndex, offsetBy: offset)
329+
} else {
330+
return try _partitionImpl(by: belongsInSecondPartition)
329331
}
330-
332+
}
333+
334+
@usableFromInline
335+
internal mutating func _partitionImpl(
336+
by belongsInSecondPartition: (Element) throws -> Bool
337+
) rethrows -> Index {
331338
var lo = startIndex
332339
var hi = endIndex
333340

stdlib/public/core/Sort.swift

Lines changed: 439 additions & 296 deletions
Large diffs are not rendered by default.

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ extension Unsafe${Mutable}BufferPointer {
408408
count = other.count
409409
}
410410

411+
@inlinable
412+
public mutating func _withUnsafeMutableBufferPointerIfSupported<R>(
413+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
414+
) rethrows -> R? {
415+
return try body(&self)
416+
}
417+
411418
% else:
412419

413420
/// Creates an immutable typed buffer pointer referencing the same memory as the

0 commit comments

Comments
 (0)