Skip to content

Commit 6d33973

Browse files
committed
Fix performance of array initialization from a generic sequence.
This fixes a major perform bug involving array initialization from any contiguously stored collection. This is not a recent regression. This fix results in a 10,000X speedup (that's 4 zeros) for this code path: func initializeFromSlice(_ a: [Int]) -> [Int] { return Array<Int>(a[...]) } A benchmark is included.
1 parent dd52105 commit 6d33973

File tree

7 files changed

+70
-17
lines changed

7 files changed

+70
-17
lines changed

Diff for: benchmark/single-source/ArrayOfGenericPOD.swift

+41-6
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,21 @@
2020

2121
import TestsUtils
2222

23-
public let ArrayOfGenericPOD = BenchmarkInfo(
24-
// Renamed benchmark to "2" when IUO test was removed, which
25-
// effectively changed what we're benchmarking here.
26-
name: "ArrayOfGenericPOD2",
27-
runFunction: run_ArrayOfGenericPOD,
28-
tags: [.validation, .api, .Array])
23+
public let ArrayOfGenericPOD = [
24+
BenchmarkInfo(
25+
// Renamed benchmark to "2" when IUO test was removed, which
26+
// effectively changed what we're benchmarking here.
27+
name: "ArrayOfGenericPOD2",
28+
runFunction: run_ArrayOfGenericPOD,
29+
tags: [.validation, .api, .Array]),
30+
31+
// Initialize an array of generic POD from a slice.
32+
// This takes a unique path through stdlib customization points.
33+
BenchmarkInfo(
34+
name: "ArrayInitFromSlice",
35+
runFunction: run_initFromSlice,
36+
tags: [.validation, .api, .Array], setUpFunction: createArrayOfPOD)
37+
]
2938

3039
class RefArray<T> {
3140
var array: [T]
@@ -64,3 +73,29 @@ public func run_ArrayOfGenericPOD(_ N: Int) {
6473
genStructArray()
6574
}
6675
}
76+
77+
// --- ArrayInitFromSlice
78+
79+
var globalArray = Array<UInt8>(repeating: 0, count: 4096)
80+
81+
func createArrayOfPOD() {
82+
blackHole(globalArray)
83+
}
84+
85+
@inline(never)
86+
@_optimize(none)
87+
func copyElements<S: Sequence>(_ contents: S) -> [UInt8]
88+
where S.Iterator.Element == UInt8
89+
{
90+
return [UInt8](contents)
91+
}
92+
93+
@inline(never)
94+
public func run_initFromSlice(_ N: Int) {
95+
for _ in 0..<N {
96+
for _ in 0..<1000 {
97+
// Slice off at least one element so the array buffer can't be reused.
98+
blackHole(copyElements(globalArray[0..<4095]))
99+
}
100+
}
101+
}

Diff for: stdlib/public/core/Array.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,7 @@ extension Array: RangeReplaceableCollection {
13061306
if let n = _buffer.requestNativeBuffer() {
13071307
return ContiguousArray(_buffer: n)
13081308
}
1309-
return _copyCollectionToContiguousArray(_buffer)
1309+
return _copyCollectionToContiguousArray(self)
13101310
}
13111311
}
13121312

Diff for: stdlib/public/core/ArrayBuffer.swift

+8
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ extension _ArrayBuffer {
223223
return UnsafeMutableRawPointer(result).assumingMemoryBound(to: Element.self)
224224
}
225225

226+
public __consuming func _copyContents(
227+
initializing buffer: UnsafeMutableBufferPointer<Element>
228+
) -> (Iterator,UnsafeMutableBufferPointer<Element>.Index) {
229+
// This customization point is not implemented for internal types.
230+
// Accidentally calling it would be a catastrophic performance bug.
231+
fatalError("unsupported")
232+
}
233+
226234
/// Returns a `_SliceBuffer` containing the given sub-range of elements in
227235
/// `bounds` from this buffer.
228236
@inlinable

Diff for: stdlib/public/core/ArraySlice.swift

+2-5
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,7 @@ extension ArraySlice: RangeReplaceableCollection {
668668
public init<S: Sequence>(_ s: S)
669669
where S.Element == Element {
670670

671-
self = ArraySlice(
672-
_buffer: _Buffer(
673-
_buffer: s._copyToContiguousArray()._buffer,
674-
shiftedToStartIndex: 0))
671+
self.init(_buffer: s._copyToContiguousArray()._buffer)
675672
}
676673

677674
/// Creates a new array containing the specified number of a single, repeated
@@ -1100,7 +1097,7 @@ extension ArraySlice: RangeReplaceableCollection {
11001097
if let n = _buffer.requestNativeBuffer() {
11011098
return ContiguousArray(_buffer: n)
11021099
}
1103-
return _copyCollectionToContiguousArray(_buffer)
1100+
return _copyCollectionToContiguousArray(self)
11041101
}
11051102
}
11061103

Diff for: stdlib/public/core/ContiguousArray.swift

+2-5
Original file line numberDiff line numberDiff line change
@@ -520,10 +520,7 @@ extension ContiguousArray: RangeReplaceableCollection {
520520
/// - Parameter s: The sequence of elements to turn into an array.
521521
@inlinable
522522
public init<S: Sequence>(_ s: S) where S.Element == Element {
523-
self = ContiguousArray(
524-
_buffer: _Buffer(
525-
_buffer: s._copyToContiguousArray()._buffer,
526-
shiftedToStartIndex: 0))
523+
self.init(_buffer: s._copyToContiguousArray()._buffer)
527524
}
528525

529526
/// Creates a new array containing the specified number of a single, repeated
@@ -948,7 +945,7 @@ extension ContiguousArray: RangeReplaceableCollection {
948945
if let n = _buffer.requestNativeBuffer() {
949946
return ContiguousArray(_buffer: n)
950947
}
951-
return _copyCollectionToContiguousArray(_buffer)
948+
return _copyCollectionToContiguousArray(self)
952949
}
953950
}
954951

Diff for: stdlib/public/core/ContiguousArrayBuffer.swift

+8
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,14 @@ internal struct _ContiguousArrayBuffer<Element> : _ArrayBufferProtocol {
398398
return target + initializedCount
399399
}
400400

401+
public __consuming func _copyContents(
402+
initializing buffer: UnsafeMutableBufferPointer<Element>
403+
) -> (Iterator,UnsafeMutableBufferPointer<Element>.Index) {
404+
// This customization point is not implemented for internal types.
405+
// Accidentally calling it would be a catastrophic performance bug.
406+
fatalError("unsupported")
407+
}
408+
401409
/// Returns a `_SliceBuffer` containing the given `bounds` of values
402410
/// from this buffer.
403411
@inlinable

Diff for: stdlib/public/core/SliceBuffer.swift

+8
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ internal struct _SliceBuffer<Element>
254254
return target + c
255255
}
256256

257+
public __consuming func _copyContents(
258+
initializing buffer: UnsafeMutableBufferPointer<Element>
259+
) -> (Iterator,UnsafeMutableBufferPointer<Element>.Index) {
260+
// This customization point is not implemented for internal types.
261+
// Accidentally calling it would be a catastrophic performance bug.
262+
fatalError("unsupported")
263+
}
264+
257265
/// True, if the array is native and does not need a deferred type check.
258266
@inlinable
259267
internal var arrayPropertyIsNativeTypeChecked: Bool {

0 commit comments

Comments
 (0)