Skip to content

[benchmark] BucketSort gardening and adjustments #24364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 29, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 41 additions & 49 deletions benchmark/single-source/BucketSort.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- BucketSort.swift ----------------------------------------------------===//
//===--- BucketSort.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
Expand All @@ -9,21 +9,21 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// Adapted from the original implementation:
// This benchmark demonstrates the benefits of ExistentialSpecializer
// optimization pass. It's a generic version of the classic BucketSort algorithm
// adapted from an original implementation from the Swift Algorithm Club.
// See https://en.wikipedia.org/wiki/Bucket_sort and
// https://github.com/raywenderlich/swift-algorithm-club/tree/master/Bucket%20Sort
// Issue: https://github.com/raywenderlich/swift-algorithm-club/issues/863

// This benchmark implements the classical BucketSort algorithm described at
// https://en.wikipedia.org/wiki/Bucket_sort. The implementation allows for an
// array of generic type of "SortableItem" to be sorted. Unfortunately, if
// ``sortingAlgo'' type is not known for the callsite in line 84, then the
// ``sort'' method can not be specialized to integer array sorting, which will
// lead to a huge performance loss. Since SortingAlgorithm and InsertionSort are
// declared to be ``public'' and that lines 83-85 can not be inlined in
// BucketSortImpl (due to inlining heuristic limitations), today swift
// compiler (without ExistentialSpecializer) can not achieve this feat. With
// ExistentialSpecializer which enables generic specialization recursively in a
// call chain, we are able to specialize line 84 for InsertionSort on integers.
//
// It sorts an array of generic `SortableItem`s. If the type of `sortingAlgo`
// is not known to the call site at line 90, the `sort` method can not be
// specialized to integer array sorting, which will lead to a huge performance
// loss. Since `SortingAlgorithm` and `InsertionSort` are declared to be
// `public` and the lines 89-91 can not be inlined in `bucketSort` (due to
// inlining heuristic limitations), compiler without ExistentialSpecializer
// optimization can not achieve this feat. With ExistentialSpecializer which
// enables generic specialization recursively in a call chain, we're able to
// specialize line 90 for `InsertionSort` on integers.

import TestsUtils
import Foundation
Expand All @@ -32,21 +32,25 @@ public let BucketSort = BenchmarkInfo(
name: "BucketSort",
runFunction: run_BucketSort,
tags: [.validation, .algorithm],
setUpFunction: { buildWorkload() },
legacyFactor: 10)
setUpFunction: { blackHole(buckets) }
)

public protocol IntegerConvertible {
func convertToInt() -> Int
}

extension Int: IntegerConvertible, SortableItem {
public func convertToInt() -> Int {
return self
}
}

public protocol SortableItem: IntegerConvertible, Comparable { }

public protocol SortingAlgorithm {
func sort<T: SortableItem>(_ items: [T]) -> [T]
}

public struct InsertionSort: SortingAlgorithm {
public func sort<T: SortableItem>(_ items: [T]) -> [T] {
var sortedItems = items
Expand All @@ -62,12 +66,14 @@ public struct InsertionSort: SortingAlgorithm {
return sortedItems
}
}

func distribute<T>(_ item: T, bucketArray: inout [Bucket<T>]) {
let val = item.convertToInt()
let capacity = bucketArray.first!.capacity
let index = val / capacity
bucketArray[index].add(item)
}

struct Bucket<T: SortableItem> {
var items: [T]
let capacity: Int
Expand All @@ -84,7 +90,10 @@ struct Bucket<T: SortableItem> {
return sortingAlgo.sort(items)
}
}
func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketArray: [Bucket<T>]) -> [T] {

func bucketSort<T>(
_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketArray: [Bucket<T>]
) -> [T] {
var copyBucketArray = bucketArray
for item in items {
distribute(item, bucketArray: &copyBucketArray)
Expand All @@ -95,45 +104,28 @@ func BucketSortImpl<T>(_ items: [T], sortingAlgorithm: SortingAlgorithm, bucketA
}
return sortedArray
}
func isArraySorted(_ arr: [Int]) -> Bool {
var idx = 0
while idx < (arr.count - 1) {
if arr[idx] > arr[idx+1] {
return false
}
idx += 1
}
return true

func isAscending(_ a: [Int]) -> Bool {
return zip(a, a.dropFirst()).allSatisfy(<=)
}
let NUMITEMS = 2500
let MAXBUCKETSIZE = 1000
let NUMBUCKETS: Int = 10

let items: [Int] = {
var array: [Int]? = [Int]()
for _ in 0..<NUMITEMS {
array!.append(Int.random(in: 0..<MAXBUCKETSIZE))
}
return array!
var g = SplitMix64(seed: 42)
return (0..<10_000).map {_ in Int.random(in: 0..<1000, using: &g) }
}()

let buckets: [Bucket<Int>] = {
let val = (items.max()?.convertToInt())! + 1
let maxCapacity = Int( ceil( Double(val) / Double(NUMBUCKETS)))
var bucketArray = [Bucket<Int>]()
for _ in 0..<NUMBUCKETS {
bucketArray.append(Bucket<Int>(capacity: maxCapacity))
}
return bucketArray
let bucketCount = 10
let maxValue = items.max()!.convertToInt()
let maxCapacity = Int(ceil(Double(maxValue + 1) / Double(bucketCount)))
return (0..<bucketCount).map { _ in Bucket<Int>(capacity: maxCapacity) }
}()

@inline(never)
func run_BucketSort(_ N : Int) {
for _ in 0..<N {
let sortedArray = BucketSortImpl(items, sortingAlgorithm: InsertionSort(), bucketArray: buckets)
CheckResults(isArraySorted(sortedArray))
let sortedArray = bucketSort(
items, sortingAlgorithm: InsertionSort(), bucketArray: buckets)
CheckResults(isAscending(sortedArray))
}
}
@inline(never)
func buildWorkload() {
blackHole(items)
blackHole(buckets)
}