From ef0fd8274930810a1b08858f130fb28d436f5d10 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 9 Jan 2024 12:01:24 -0800 Subject: [PATCH 1/2] [stdlib] always bounds check in UnsafeBufferPointer --- .../public/core/UnsafeBufferPointer.swift.gyb | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/stdlib/public/core/UnsafeBufferPointer.swift.gyb b/stdlib/public/core/UnsafeBufferPointer.swift.gyb index f110cc6c6e5db..14f5abe97d291 100644 --- a/stdlib/public/core/UnsafeBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeBufferPointer.swift.gyb @@ -304,16 +304,15 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle @inlinable // unsafe-performance public func _failEarlyRangeCheck(_ index: Int, bounds: Range) { - // NOTE: In release mode, this method is a no-op for performance reasons. - _debugPrecondition(index >= bounds.lowerBound) - _debugPrecondition(index < bounds.upperBound) + _precondition(index >= bounds.lowerBound && index < bounds.upperBound) } @inlinable // unsafe-performance public func _failEarlyRangeCheck(_ range: Range, bounds: Range) { - // NOTE: In release mode, this method is a no-op for performance reasons. - _debugPrecondition(range.lowerBound >= bounds.lowerBound) - _debugPrecondition(range.upperBound <= bounds.upperBound) + _precondition( + range.lowerBound >= bounds.lowerBound && + range.upperBound <= bounds.upperBound + ) } @inlinable // unsafe-performance @@ -365,14 +364,14 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle @inlinable // unsafe-performance public subscript(i: Int) -> Element { get { - _debugPrecondition(i >= 0) - _debugPrecondition(i < endIndex) + _precondition(i >= 0) + _precondition(i < endIndex) return _position._unsafelyUnwrappedUnchecked[i] } %if Mutable: nonmutating _modify { - _debugPrecondition(i >= 0) - _debugPrecondition(i < endIndex) + _precondition(i >= 0) + _precondition(i < endIndex) yield &_position._unsafelyUnwrappedUnchecked[i] } %end @@ -438,16 +437,16 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle -> Slice> { get { - _debugPrecondition(bounds.lowerBound >= startIndex) - _debugPrecondition(bounds.upperBound <= endIndex) + _precondition(bounds.lowerBound >= startIndex) + _precondition(bounds.upperBound <= endIndex) return Slice( base: self, bounds: bounds) } % if Mutable: nonmutating set { - _debugPrecondition(bounds.lowerBound >= startIndex) - _debugPrecondition(bounds.upperBound <= endIndex) - _debugPrecondition(bounds.count == newValue.count) + _precondition(bounds.lowerBound >= startIndex) + _precondition(bounds.upperBound <= endIndex) + _precondition(bounds.count == newValue.count) // FIXME: swift-3-indexing-model: tests. if !newValue.isEmpty { @@ -472,8 +471,8 @@ extension Unsafe${Mutable}BufferPointer: ${Mutable}Collection, RandomAccessColle @inlinable // unsafe-performance public func swapAt(_ i: Int, _ j: Int) { guard i != j else { return } - _debugPrecondition(i >= 0 && j >= 0) - _debugPrecondition(i < endIndex && j < endIndex) + _precondition(i >= 0 && j >= 0) + _precondition(i < endIndex && j < endIndex) let pi = (_position! + i) let pj = (_position! + j) let tmp = pi.move() @@ -1029,7 +1028,7 @@ extension Unsafe${Mutable}BufferPointer { @inlinable @_alwaysEmitIntoClient public func initializeElement(at index: Index, to value: Element) { - _debugPrecondition(startIndex <= index && index < endIndex) + _precondition(startIndex <= index && index < endIndex) let p = baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index) p.initialize(to: value) } @@ -1047,7 +1046,7 @@ extension Unsafe${Mutable}BufferPointer { @inlinable @_alwaysEmitIntoClient public func moveElement(from index: Index) -> Element { - _debugPrecondition(startIndex <= index && index < endIndex) + _precondition(startIndex <= index && index < endIndex) return baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index).move() } @@ -1062,7 +1061,7 @@ extension Unsafe${Mutable}BufferPointer { @inlinable @_alwaysEmitIntoClient public func deinitializeElement(at index: Index) { - _debugPrecondition(startIndex <= index && index < endIndex) + _precondition(startIndex <= index && index < endIndex) let p = baseAddress._unsafelyUnwrappedUnchecked.advanced(by: index) p.deinitialize(count: 1) } From 4348300e1afe1319b1792828b0c24c29ec275a60 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 9 Jan 2024 16:01:46 -0800 Subject: [PATCH 2/2] [benchmark] specialize _insertionSort for UMBP - hoist bounds-checking out of the insertion sort loop --- stdlib/public/core/Sort.swift | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/stdlib/public/core/Sort.swift b/stdlib/public/core/Sort.swift index 46e59b108d53d..eaaf4e2f56392 100644 --- a/stdlib/public/core/Sort.swift +++ b/stdlib/public/core/Sort.swift @@ -710,4 +710,45 @@ extension UnsafeMutableBufferPointer { _internalInvariant(runs.count == 1, "Didn't complete final merge") } } + + /// Sorts `self[range]` according to `areInIncreasingOrder`. Stable. + /// + /// - Precondition: `sortedEnd != range.lowerBound` + /// - Precondition: `elements[.., + sortedEnd: Index, + by areInIncreasingOrder: (Element, Element) throws -> Bool + ) rethrows { + _precondition(sortedEnd > range.lowerBound && sortedEnd <= range.upperBound) + var sortedEnd = sortedEnd + + // Continue sorting until the sorted elements cover the whole range. + while sortedEnd != range.upperBound { + var i = sortedEnd + // Look backwards for `self[i]`'s position in the sorted sequence, + // moving each element forward to make room. + repeat { + let j = index(before: i) + + let pi = (_position._unsafelyUnwrappedUnchecked + i) + let pj = (_position._unsafelyUnwrappedUnchecked + j) + + // If `self[i]` doesn't belong before `self[j]`, we've found + // its position. + if try !areInIncreasingOrder(pi.pointee, pj.pointee) { + break + } + + let tmp = pi.move() + pi.moveInitialize(from: pj, count: 1) + pj.initialize(to: consume tmp) + + i = j + } while i != range.lowerBound + + formIndex(after: &sortedEnd) + } + } }