Skip to content

Commit fc36bed

Browse files
committed
[stdlib] Improve performance of Array initialization with known capacity
1 parent b9b0b7e commit fc36bed

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

stdlib/public/core/Collection.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,17 +1325,31 @@ extension Collection {
13251325
return []
13261326
}
13271327

1328-
var result = ContiguousArray<T>()
1329-
result.reserveCapacity(n)
1328+
let result: ContiguousArray<T>
1329+
var p: UnsafeMutablePointer<T>
1330+
(result, p) = ContiguousArray<T>._allocateUninitialized(n)
13301331

13311332
var i = self.startIndex
1333+
var initializedElements = 0
13321334

1333-
for _ in 0..<n {
1334-
result.append(try transform(self[i]))
1335-
formIndex(after: &i)
1335+
do {
1336+
for _ in 0..<n {
1337+
(p + initializedElements).initialize(to: try transform(self[i]))
1338+
1339+
formIndex(after: &i)
1340+
initializedElements += 1
1341+
}
1342+
} catch {
1343+
// The buffer may not have been initialized entirely. Set its count to
1344+
// number of actually initialized items to avoid accessing uninitialized
1345+
// memory
1346+
result._buffer.count = initializedElements
1347+
throw error
13361348
}
13371349

13381350
_expectEnd(of: self, is: i)
1351+
_debugPrecondition(initializedElements == n,
1352+
"buffer not completely initialized")
13391353
return Array(result)
13401354
}
13411355

stdlib/public/core/Sequence.swift

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -822,15 +822,31 @@ extension Sequence {
822822
_ transform: (Element) throws -> T
823823
) rethrows -> [T] {
824824
let initialCapacity = underestimatedCount
825-
var result = ContiguousArray<T>()
826-
result.reserveCapacity(initialCapacity)
825+
var result: ContiguousArray<T>
826+
var p: UnsafeMutablePointer<T>
827+
(result, p) = ContiguousArray<T>._allocateUninitialized(initialCapacity)
827828

828829
var iterator = self.makeIterator()
829830

830-
// Add elements up to the initial capacity without checking for regrowth.
831-
for _ in 0..<initialCapacity {
832-
result.append(try transform(iterator.next()!))
831+
var initializedElements = 0
832+
833+
// Initialize the initial capacity of the array without needing to regrow it
834+
do {
835+
for _ in 0..<initialCapacity {
836+
(p + initializedElements).initialize(to: try transform(iterator.next()!))
837+
initializedElements += 1
838+
}
839+
} catch {
840+
// The buffer may not have been initialized entirely. Set its count to
841+
// number of actually initialized items to avoid accessing uninitialized
842+
// memory
843+
result._buffer.count = initializedElements
844+
throw error
833845
}
846+
847+
_debugPrecondition(initializedElements == initialCapacity,
848+
"buffer not completely initialized")
849+
834850
// Add remaining elements, if any.
835851
while let element = iterator.next() {
836852
result.append(try transform(element))

0 commit comments

Comments
 (0)