Skip to content

Commit 8103416

Browse files
authored
Merge pull request #24364 from palimondo/bucketsort-gardening
[benchmark] BucketSort gardening and adjustments
2 parents d044696 + 5032d33 commit 8103416

File tree

1 file changed

+41
-49
lines changed

1 file changed

+41
-49
lines changed
Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- BucketSort.swift ----------------------------------------------------===//
1+
//===--- BucketSort.swift -------------------------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -9,21 +9,21 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
// Adapted from the original implementation:
12+
// This benchmark demonstrates the benefits of ExistentialSpecializer
13+
// optimization pass. It's a generic version of the classic BucketSort algorithm
14+
// adapted from an original implementation from the Swift Algorithm Club.
15+
// See https://en.wikipedia.org/wiki/Bucket_sort and
1316
// https://github.com/raywenderlich/swift-algorithm-club/tree/master/Bucket%20Sort
14-
// Issue: https://github.com/raywenderlich/swift-algorithm-club/issues/863
15-
16-
// This benchmark implements the classical BucketSort algorithm described at
17-
// https://en.wikipedia.org/wiki/Bucket_sort. The implementation allows for an
18-
// array of generic type of "SortableItem" to be sorted. Unfortunately, if
19-
// ``sortingAlgo'' type is not known for the callsite in line 84, then the
20-
// ``sort'' method can not be specialized to integer array sorting, which will
21-
// lead to a huge performance loss. Since SortingAlgorithm and InsertionSort are
22-
// declared to be ``public'' and that lines 83-85 can not be inlined in
23-
// BucketSortImpl (due to inlining heuristic limitations), today swift
24-
// compiler (without ExistentialSpecializer) can not achieve this feat. With
25-
// ExistentialSpecializer which enables generic specialization recursively in a
26-
// call chain, we are able to specialize line 84 for InsertionSort on integers.
17+
//
18+
// It sorts an array of generic `SortableItem`s. If the type of `sortingAlgo`
19+
// is not known to the call site at line 90, the `sort` method can not be
20+
// specialized to integer array sorting, which will lead to a huge performance
21+
// loss. Since `SortingAlgorithm` and `InsertionSort` are declared to be
22+
// `public` and the lines 89-91 can not be inlined in `bucketSort` (due to
23+
// inlining heuristic limitations), compiler without ExistentialSpecializer
24+
// optimization can not achieve this feat. With ExistentialSpecializer which
25+
// enables generic specialization recursively in a call chain, we're able to
26+
// specialize line 90 for `InsertionSort` on integers.
2727

2828
import TestsUtils
2929
import Foundation
@@ -32,21 +32,25 @@ public let BucketSort = BenchmarkInfo(
3232
name: "BucketSort",
3333
runFunction: run_BucketSort,
3434
tags: [.validation, .algorithm],
35-
setUpFunction: { buildWorkload() },
36-
legacyFactor: 10)
35+
setUpFunction: { blackHole(buckets) }
36+
)
3737

3838
public protocol IntegerConvertible {
3939
func convertToInt() -> Int
4040
}
41+
4142
extension Int: IntegerConvertible, SortableItem {
4243
public func convertToInt() -> Int {
4344
return self
4445
}
4546
}
47+
4648
public protocol SortableItem: IntegerConvertible, Comparable { }
49+
4750
public protocol SortingAlgorithm {
4851
func sort<T: SortableItem>(_ items: [T]) -> [T]
4952
}
53+
5054
public struct InsertionSort: SortingAlgorithm {
5155
public func sort<T: SortableItem>(_ items: [T]) -> [T] {
5256
var sortedItems = items
@@ -62,12 +66,14 @@ public struct InsertionSort: SortingAlgorithm {
6266
return sortedItems
6367
}
6468
}
69+
6570
func distribute<T>(_ item: T, bucketArray: inout [Bucket<T>]) {
6671
let val = item.convertToInt()
6772
let capacity = bucketArray.first!.capacity
6873
let index = val / capacity
6974
bucketArray[index].add(item)
7075
}
76+
7177
struct Bucket<T: SortableItem> {
7278
var items: [T]
7379
let capacity: Int
@@ -84,7 +90,10 @@ struct Bucket<T: SortableItem> {
8490
return sortingAlgo.sort(items)
8591
}
8692
}
87-
func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketArray: [Bucket<T>]) -> [T] {
93+
94+
func bucketSort<T>(
95+
_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketArray: [Bucket<T>]
96+
) -> [T] {
8897
var copyBucketArray = bucketArray
8998
for item in items {
9099
distribute(item, bucketArray: &copyBucketArray)
@@ -95,45 +104,28 @@ func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketA
95104
}
96105
return sortedArray
97106
}
98-
func isArraySorted(_ arr: [Int]) -> Bool {
99-
var idx = 0
100-
while idx < (arr.count - 1) {
101-
if arr[idx] > arr[idx+1] {
102-
return false
103-
}
104-
idx += 1
105-
}
106-
return true
107+
108+
func isAscending(_ a: [Int]) -> Bool {
109+
return zip(a, a.dropFirst()).allSatisfy(<=)
107110
}
108-
let NUMITEMS = 2500
109-
let MAXBUCKETSIZE = 1000
110-
let NUMBUCKETS: Int = 10
111+
111112
let items: [Int] = {
112-
var array: [Int]? = [Int]()
113-
for _ in 0..<NUMITEMS {
114-
array!.append(Int.random(in: 0..<MAXBUCKETSIZE))
115-
}
116-
return array!
113+
var g = SplitMix64(seed: 42)
114+
return (0..<10_000).map {_ in Int.random(in: 0..<1000, using: &g) }
117115
}()
118116

119117
let buckets: [Bucket<Int>] = {
120-
let val = (items.max()?.convertToInt())! + 1
121-
let maxCapacity = Int( ceil( Double(val) / Double(NUMBUCKETS)))
122-
var bucketArray = [Bucket<Int>]()
123-
for _ in 0..<NUMBUCKETS {
124-
bucketArray.append(Bucket<Int>(capacity: maxCapacity))
125-
}
126-
return bucketArray
118+
let bucketCount = 10
119+
let maxValue = items.max()!.convertToInt()
120+
let maxCapacity = Int(ceil(Double(maxValue + 1) / Double(bucketCount)))
121+
return (0..<bucketCount).map { _ in Bucket<Int>(capacity: maxCapacity) }
127122
}()
123+
128124
@inline(never)
129125
func run_BucketSort(_ N : Int) {
130126
for _ in 0..<N {
131-
let sortedArray = BucketSortImpl(items, sortingAlgorithm: InsertionSort(), bucketArray: buckets)
132-
CheckResults(isArraySorted(sortedArray))
127+
let sortedArray = bucketSort(
128+
items, sortingAlgorithm: InsertionSort(), bucketArray: buckets)
129+
CheckResults(isAscending(sortedArray))
133130
}
134131
}
135-
@inline(never)
136-
func buildWorkload() {
137-
blackHole(items)
138-
blackHole(buckets)
139-
}

0 commit comments

Comments
 (0)