Skip to content

Commit f492087

Browse files
authored
[stdlib] performance optimizations in Array.replaceSubrange (#66160)
1 parent ffc2d9f commit f492087

File tree

1 file changed

+31
-62
lines changed

1 file changed

+31
-62
lines changed

stdlib/public/core/ArrayBufferProtocol.swift

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -149,75 +149,44 @@ extension _ArrayBufferProtocol {
149149
elementsOf newValues: __owned C
150150
) where C: Collection, C.Element == Element {
151151
_internalInvariant(startIndex == 0, "_SliceBuffer should override this function.")
152-
let oldCount = self.count
152+
let elements = self.firstElementAddress
153+
154+
// erase all the elements we're replacing to create a hole
155+
let holeStart = elements + subrange.lowerBound
156+
let holeEnd = holeStart + newCount
153157
let eraseCount = subrange.count
158+
holeStart.deinitialize(count: eraseCount)
154159

155160
let growth = newCount - eraseCount
156-
// This check will prevent storing a 0 count to the empty array singleton.
157-
if growth != 0 {
158-
self.count = oldCount + growth
159-
}
160161

161-
let elements = self.subscriptBaseAddress
162-
let oldTailIndex = subrange.upperBound
163-
let oldTailStart = elements + oldTailIndex
164-
let newTailIndex = oldTailIndex + growth
165-
let newTailStart = oldTailStart + growth
166-
let tailCount = oldCount - subrange.upperBound
167-
168-
if growth > 0 {
169-
// Slide the tail part of the buffer forwards, in reverse order
170-
// so as not to self-clobber.
171-
newTailStart.moveInitialize(from: oldTailStart, count: tailCount)
172-
173-
// Update the original subrange
174-
var i = newValues.startIndex
175-
for j in subrange {
176-
elements[j] = newValues[i]
177-
newValues.formIndex(after: &i)
178-
}
179-
// Initialize the hole left by sliding the tail forward
180-
for j in oldTailIndex..<newTailIndex {
181-
(elements + j).initialize(to: newValues[i])
182-
newValues.formIndex(after: &i)
183-
}
184-
_expectEnd(of: newValues, is: i)
162+
if growth != 0 {
163+
let tailStart = elements + subrange.upperBound
164+
let tailCount = self.count - subrange.upperBound
165+
holeEnd.moveInitialize(from: tailStart, count: tailCount)
166+
self.count += growth
185167
}
186-
else { // We're not growing the buffer
187-
// Assign all the new elements into the start of the subrange
188-
var i = subrange.lowerBound
189-
var j = newValues.startIndex
190-
for _ in 0..<newCount {
191-
elements[i] = newValues[j]
192-
i += 1
193-
newValues.formIndex(after: &j)
194-
}
195-
_expectEnd(of: newValues, is: j)
196-
197-
// If the size didn't change, we're done.
198-
if growth == 0 {
199-
return
200-
}
201168

202-
// Move the tail backward to cover the shrinkage.
203-
let shrinkage = -growth
204-
if tailCount > shrinkage { // If the tail length exceeds the shrinkage
205-
206-
// Update the rest of the replaced range with the first
207-
// part of the tail.
208-
newTailStart.moveUpdate(from: oldTailStart, count: shrinkage)
209-
210-
// Slide the rest of the tail back
211-
oldTailStart.moveInitialize(
212-
from: oldTailStart + shrinkage, count: tailCount - shrinkage)
169+
// don't use UnsafeMutableBufferPointer.initialize(fromContentsOf:)
170+
// since it behaves differently on collections that misreport count,
171+
// and breaks validation tests for those usecases / potentially
172+
// breaks ABI guarantees.
173+
if newCount > 0 {
174+
let done: Void? = newValues.withContiguousStorageIfAvailable {
175+
_precondition(
176+
$0.count == newCount,
177+
"invalid Collection: count differed in successive traversals"
178+
)
179+
holeStart.initialize(from: $0.baseAddress!, count: newCount)
213180
}
214-
else { // Tail fits within erased elements
215-
// Update the start of the replaced range with the tail
216-
newTailStart.moveUpdate(from: oldTailStart, count: tailCount)
217-
218-
// Destroy elements remaining after the tail in subrange
219-
(newTailStart + tailCount).deinitialize(
220-
count: shrinkage - tailCount)
181+
if done == nil {
182+
var place = holeStart
183+
var i = newValues.startIndex
184+
while place < holeEnd {
185+
place.initialize(to: newValues[i])
186+
place += 1
187+
newValues.formIndex(after: &i)
188+
}
189+
_expectEnd(of: newValues, is: i)
221190
}
222191
}
223192
}

0 commit comments

Comments
 (0)