|
| 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: ©BucketArray) |
| 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