-
Notifications
You must be signed in to change notification settings - Fork 297
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a min-max heap implementation that can be used to back a priority…
… queue (#61) * Add PriorityQueue implementation built on top of a MinMaxHeap * Merge MinMaxHeap into PriorityQueue * Add `unordered` read-only view into underlying heap * Start filling in PriorityQueue benchmarks * Implement our own swapAt() storage.swapAt exhibits excessive allocations * Rename _delete(at:) to _remove(at:) * Rename removeMin/removeMax -> popMin/popMax * Use magic values 1, 2 to refer to the items in the first max level Co-authored-by: Tim Vermeulen <tvermeulen@apple.com> * Specify logarithmic complexities as "O(log `count`) / 2" instead of "O(log n)" Co-authored-by: Tim Vermeulen <tvermeulen@apple.com> * Clarify logic in popMax * Simplify logic in _remove(at:) * Move the bounds checking into the various index computation methods They will all now return `nil` for invalid indices. * Floyd's heap construction algorithm should start from count/2 - 1 * Add ObjC wrapper around CFBinaryHeap to benchmarks * Fix benchmarks that broke because of renames * Add removeMin/removeMax * Make _minMaxHeapIsMinLevel an instance method * Split _indexOfChildOrGrandchild(of:sortedUsing:) into two separate functions There is a large performance cost with passing a predicate function. * Defer comparing children when determining largest/smallest descendant If the given index has 4 grandchildren, we can skip comparing the children. * Added an iterator of the min and max views to the priority queue * seperated iterator implementation into a new file * Use renamed popMin/Max in Iterator instead of removeMin/Max * Fix code formatting Indentation of 2 spaces and 80 char column width * Add sequence initializer * Fix code formatting in benchmarks * Fix benchmark names * Remove init from Collection This should already be handled by the init from Sequence * Add conformance to ExpressibleByArrayLiteral * Move ExpressibleByArrayLiteral conformance to separate file * Update PriorityQueue's CMakeLists.txt * Make ascending and descending iterators public * Inline ALL THE THINGS! * Iterative instead of recursive implementation, @inline(__always) a couple more critical functions. * Address PR feedback, thanks! * Check invariants on insertion and deletion * Minor code formatting cleanup * Add copyright header to test file * Fix missing empty line * Add naïve implementation of insert(contentsOf:) * Add documentation on complexity of init<S:Sequence>(_:) * Cite source paper in documentation * Rename PriorityQueue -> MinMaxHeap We'll be reintroducing the PriorityQueue type as a wrapper. * Mark insert(contentsOf:) as inlinable This results in ~10x speedup in my initial tests. * Rename argument label "startingAt" -> "elementAt" These functions used to be recursive, so "startingAt" made sense. Now that they're iterative, we should fix the label. * Remove coefficients from complexity docs * Make `_minMaxHeapIsMinLevel` take an index instead of a count * Rename MinMaxHeap -> Heap * Add documentation for Heap * Make Heap.Iterator init and direction internal * Fix reference to queue in documentation Co-authored-by: Dante Broggi <34220985+Dante-Broggi@users.noreply.github.com> * Don't wrap integers in CFBinaryHeap benchmark in NSNumber * Avoid heap allocation altogether * Add table with performance of operations * Add heap performance graph * Apply suggestions from code review Co-authored-by: Karoy Lorentey <klorentey@apple.com> * Make _checkInvariants comments a doc comment * Split Heap.Iterator into two separate views Adapted from Daryle Walker's (github.com/CTMacUser) suggestions * Prefix heap storage variable with an underscore * Update benchmark image * Fix Sources/PriorityQueueModule/CMakeLists.txt Heap+Iterator.swift was renamed to Heap+OrderedViews.swift Co-authored-by: Karoy Lorentey <klorentey@apple.com> * CFBinaryHeap is only available on Darwin * CFBinaryHeap is only available on Darwin * CFBinaryHeap is only available on Darwin Co-authored-by: Tim Vermeulen <tvermeulen@apple.com> Co-authored-by: Amanuel Ephem <amanuelephrem776@gmail.com> Co-authored-by: Joakim Hassila <joj@mac.com> Co-authored-by: Dante Broggi <34220985+Dante-Broggi@users.noreply.github.com> Co-authored-by: Karoy Lorentey <klorentey@apple.com>
- Loading branch information
1 parent
0959ba7
commit 940c963
Showing
22 changed files
with
1,721 additions
and
3 deletions.
There are no files selected for viewing
77 changes: 77 additions & 0 deletions
77
.swiftpm/xcode/xcshareddata/xcschemes/PriorityQueueModule.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Scheme | ||
LastUpgradeVersion = "1250" | ||
version = "1.3"> | ||
<BuildAction | ||
parallelizeBuildables = "YES" | ||
buildImplicitDependencies = "YES"> | ||
<BuildActionEntries> | ||
<BuildActionEntry | ||
buildForTesting = "YES" | ||
buildForRunning = "YES" | ||
buildForProfiling = "YES" | ||
buildForArchiving = "YES" | ||
buildForAnalyzing = "YES"> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "PriorityQueueModule" | ||
BuildableName = "PriorityQueueModule" | ||
BlueprintName = "PriorityQueueModule" | ||
ReferencedContainer = "container:"> | ||
</BuildableReference> | ||
</BuildActionEntry> | ||
</BuildActionEntries> | ||
</BuildAction> | ||
<TestAction | ||
buildConfiguration = "Debug" | ||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
shouldUseLaunchSchemeArgsEnv = "YES"> | ||
<Testables> | ||
<TestableReference | ||
skipped = "NO"> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "PriorityQueueTests" | ||
BuildableName = "PriorityQueueTests" | ||
BlueprintName = "PriorityQueueTests" | ||
ReferencedContainer = "container:"> | ||
</BuildableReference> | ||
</TestableReference> | ||
</Testables> | ||
</TestAction> | ||
<LaunchAction | ||
buildConfiguration = "Debug" | ||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
launchStyle = "0" | ||
useCustomWorkingDirectory = "NO" | ||
ignoresPersistentStateOnLaunch = "NO" | ||
debugDocumentVersioning = "YES" | ||
debugServiceExtension = "internal" | ||
allowLocationSimulation = "YES"> | ||
</LaunchAction> | ||
<ProfileAction | ||
buildConfiguration = "Release" | ||
shouldUseLaunchSchemeArgsEnv = "YES" | ||
savedToolIdentifier = "" | ||
useCustomWorkingDirectory = "NO" | ||
debugDocumentVersioning = "YES"> | ||
<MacroExpansion> | ||
<BuildableReference | ||
BuildableIdentifier = "primary" | ||
BlueprintIdentifier = "PriorityQueueModule" | ||
BuildableName = "PriorityQueueModule" | ||
BlueprintName = "PriorityQueueModule" | ||
ReferencedContainer = "container:"> | ||
</BuildableReference> | ||
</MacroExpansion> | ||
</ProfileAction> | ||
<AnalyzeAction | ||
buildConfiguration = "Debug"> | ||
</AnalyzeAction> | ||
<ArchiveAction | ||
buildConfiguration = "Release" | ||
revealArchiveInOrganizer = "YES"> | ||
</ArchiveAction> | ||
</Scheme> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift Collections open source project | ||
// | ||
// Copyright (c) 2021 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import CollectionsBenchmark | ||
import PriorityQueueModule | ||
import CppBenchmarks | ||
|
||
extension Benchmark { | ||
public mutating func addHeapBenchmarks() { | ||
self.addSimple( | ||
title: "Heap<Int> init from range", | ||
input: Int.self | ||
) { size in | ||
blackHole(Heap(0..<size)) | ||
} | ||
|
||
self.addSimple( | ||
title: "Heap<Int> insert", | ||
input: [Int].self | ||
) { input in | ||
var queue = Heap<Int>() | ||
for i in input { | ||
queue.insert(i) | ||
} | ||
precondition(queue.count == input.count) | ||
blackHole(queue) | ||
} | ||
|
||
self.add( | ||
title: "Heap<Int> insert(contentsOf:)", | ||
input: ([Int], [Int]).self | ||
) { (existing, new) in | ||
return { timer in | ||
var queue = Heap(existing) | ||
queue.insert(contentsOf: new) | ||
precondition(queue.count == existing.count + new.count) | ||
blackHole(queue) | ||
} | ||
} | ||
|
||
self.add( | ||
title: "Heap<Int> popMax", | ||
input: [Int].self | ||
) { input in | ||
return { timer in | ||
var queue = Heap(input) | ||
timer.measure { | ||
while let max = queue.popMax() { | ||
blackHole(max) | ||
} | ||
} | ||
precondition(queue.isEmpty) | ||
blackHole(queue) | ||
} | ||
} | ||
|
||
self.add( | ||
title: "Heap<Int> popMin", | ||
input: [Int].self | ||
) { input in | ||
return { timer in | ||
var queue = Heap(input) | ||
timer.measure { | ||
while let min = queue.popMin() { | ||
blackHole(min) | ||
} | ||
} | ||
precondition(queue.isEmpty) | ||
blackHole(queue) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// MARK: - | ||
|
||
extension Benchmark { | ||
public mutating func addCFBinaryHeapBenchmarks() { | ||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
self.addSimple( | ||
title: "CFBinaryHeap insert", | ||
input: [Int].self | ||
) { input in | ||
let heap = BinaryHeap() | ||
for i in input { | ||
heap.insert(i) | ||
} | ||
precondition(heap.count == input.count) | ||
blackHole(heap) | ||
} | ||
|
||
self.add( | ||
title: "CFBinaryHeap removeMinimumValue", | ||
input: [Int].self | ||
) { input in | ||
return { timer in | ||
let heap = BinaryHeap() | ||
for i in input { | ||
heap.insert(i) | ||
} | ||
|
||
timer.measure { | ||
while heap.count > 0 { | ||
let min = heap.popMinimum() | ||
blackHole(min) | ||
} | ||
} | ||
precondition(heap.count == 0) | ||
blackHole(heap) | ||
} | ||
} | ||
#endif | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift Collections open source project | ||
// | ||
// Copyright (c) 2021 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef BinaryHeap_h | ||
#define BinaryHeap_h | ||
|
||
#if __APPLE__ // CFBinaryHeap only exists on Apple platforms | ||
|
||
@import Foundation; | ||
|
||
@interface BinaryHeap: NSObject | ||
|
||
@property (nonatomic, readonly) NSUInteger count; | ||
|
||
- (void)insert:(NSInteger)value; | ||
- (NSInteger)popMinimum; | ||
|
||
@end | ||
|
||
#endif // __APPLE__ | ||
#endif /* BinaryHeap_h */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.