Skip to content

Commit cfc8409

Browse files
authored
Merge pull request #24065 from rajbarik/raj-es-perf-bench
Performance Benchmarking of ExistentialSpecializer
2 parents 41d5cb0 + 78a0fe1 commit cfc8409

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ set(SWIFT_BENCH_MODULES
4343
single-source/BinaryFloatingPointProperties
4444
single-source/BitCount
4545
single-source/Breadcrumbs
46+
single-source/BucketSort
4647
single-source/ByteSwap
4748
single-source/COWTree
4849
single-source/COWArrayGuaranteedParameterOverhead
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===--- BucketSort.swift ----------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// Adapted from the original implementation:
13+
// 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.
27+
28+
import TestsUtils
29+
import Foundation
30+
31+
public let BucketSort = BenchmarkInfo(
32+
name: "BucketSort",
33+
runFunction: run_BucketSort,
34+
tags: [.validation, .algorithm],
35+
setUpFunction: { buildWorkload() },
36+
legacyFactor: 10)
37+
38+
public protocol IntegerConvertible {
39+
func convertToInt() -> Int
40+
}
41+
extension Int: IntegerConvertible, SortableItem {
42+
public func convertToInt() -> Int {
43+
return self
44+
}
45+
}
46+
public protocol SortableItem: IntegerConvertible, Comparable { }
47+
public protocol SortingAlgorithm {
48+
func sort<T: SortableItem>(_ items: [T]) -> [T]
49+
}
50+
public struct InsertionSort: SortingAlgorithm {
51+
public func sort<T: SortableItem>(_ items: [T]) -> [T] {
52+
var sortedItems = items
53+
for i in 0 ..< sortedItems.count {
54+
var j = i
55+
while j > 0 && sortedItems[j-1] > sortedItems[j] {
56+
let temp = sortedItems[j-1]
57+
sortedItems[j-1] = sortedItems[j]
58+
sortedItems[j] = temp
59+
j -= 1
60+
}
61+
}
62+
return sortedItems
63+
}
64+
}
65+
func distribute<T>(_ item: T, bucketArray: inout [Bucket<T>]) {
66+
let val = item.convertToInt()
67+
let capacity = bucketArray.first!.capacity
68+
let index = val / capacity
69+
bucketArray[index].add(item)
70+
}
71+
struct Bucket<T: SortableItem> {
72+
var items: [T]
73+
let capacity: Int
74+
init(capacity: Int) {
75+
self.capacity = capacity
76+
items = [T]()
77+
}
78+
mutating func add(_ item: T) {
79+
if items.count < capacity {
80+
items.append(item)
81+
}
82+
}
83+
func sort(_ sortingAlgo: SortingAlgorithm) -> [T] {
84+
return sortingAlgo.sort(items)
85+
}
86+
}
87+
func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketArray: [Bucket<T>]) -> [T] {
88+
var copyBucketArray = bucketArray
89+
for item in items {
90+
distribute(item, bucketArray: &copyBucketArray)
91+
}
92+
var sortedArray = [T]()
93+
for bucket in copyBucketArray {
94+
sortedArray += bucket.sort(sortingAlgorithm)
95+
}
96+
return sortedArray
97+
}
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+
let NUMITEMS = 10000
109+
let MAXBUCKETSIZE = 1000
110+
let NUMBUCKETS: Int = 10
111+
let items: [Int] = {
112+
var array: [Int]? = [Int]()
113+
for _ in 0..<NUMITEMS {
114+
array!.append( Int(arc4random_uniform( UInt32( MAXBUCKETSIZE ) ) ) )
115+
}
116+
return array!
117+
}()
118+
119+
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
127+
}()
128+
@inline(never)
129+
func run_BucketSort(_ N : Int) {
130+
for _ in 0..<N {
131+
let sortedArray = BucketSortImpl(items, sortingAlgorithm: InsertionSort(), bucketArray: buckets)
132+
CheckResults(isArraySorted(sortedArray))
133+
}
134+
}
135+
@inline(never)
136+
func buildWorkload() {
137+
blackHole(items)
138+
blackHole(buckets)
139+
}

0 commit comments

Comments
 (0)