1
- //===--- BucketSort.swift ---------------------------------------------------- ===//
1
+ //===--- BucketSort.swift -------------------------------------------------===//
2
2
//
3
3
// This source file is part of the Swift.org open source project
4
4
//
9
9
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10
10
//
11
11
//===----------------------------------------------------------------------===//
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
13
16
// 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.
27
27
28
28
import TestsUtils
29
29
import Foundation
@@ -32,21 +32,25 @@ public let BucketSort = BenchmarkInfo(
32
32
name: " BucketSort " ,
33
33
runFunction: run_BucketSort,
34
34
tags: [ . validation, . algorithm] ,
35
- setUpFunction: { buildWorkload ( ) } ,
36
- legacyFactor : 10 )
35
+ setUpFunction: { blackHole ( buckets ) }
36
+ )
37
37
38
38
public protocol IntegerConvertible {
39
39
func convertToInt( ) -> Int
40
40
}
41
+
41
42
extension Int : IntegerConvertible , SortableItem {
42
43
public func convertToInt( ) -> Int {
43
44
return self
44
45
}
45
46
}
47
+
46
48
public protocol SortableItem : IntegerConvertible , Comparable { }
49
+
47
50
public protocol SortingAlgorithm {
48
51
func sort< T: SortableItem > ( _ items: [ T ] ) -> [ T ]
49
52
}
53
+
50
54
public struct InsertionSort : SortingAlgorithm {
51
55
public func sort< T: SortableItem > ( _ items: [ T ] ) -> [ T ] {
52
56
var sortedItems = items
@@ -62,12 +66,14 @@ public struct InsertionSort: SortingAlgorithm {
62
66
return sortedItems
63
67
}
64
68
}
69
+
65
70
func distribute< T> ( _ item: T , bucketArray: inout [ Bucket < T > ] ) {
66
71
let val = item. convertToInt ( )
67
72
let capacity = bucketArray. first!. capacity
68
73
let index = val / capacity
69
74
bucketArray [ index] . add ( item)
70
75
}
76
+
71
77
struct Bucket < T: SortableItem > {
72
78
var items : [ T ]
73
79
let capacity : Int
@@ -84,7 +90,10 @@ struct Bucket<T: SortableItem> {
84
90
return sortingAlgo. sort ( items)
85
91
}
86
92
}
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 ] {
88
97
var copyBucketArray = bucketArray
89
98
for item in items {
90
99
distribute ( item, bucketArray: & copyBucketArray)
@@ -95,45 +104,28 @@ func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketA
95
104
}
96
105
return sortedArray
97
106
}
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 ( <= )
107
110
}
108
- let NUMITEMS = 2500
109
- let MAXBUCKETSIZE = 1000
110
- let NUMBUCKETS : Int = 10
111
+
111
112
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) }
117
115
} ( )
118
116
119
117
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) }
127
122
} ( )
123
+
128
124
@inline ( never)
129
125
func run_BucketSort( _ N : Int ) {
130
126
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) )
133
130
}
134
131
}
135
- @inline ( never)
136
- func buildWorkload( ) {
137
- blackHole ( items)
138
- blackHole ( buckets)
139
- }
0 commit comments