-
Notifications
You must be signed in to change notification settings - Fork 285
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
Add a min-max heap implementation that can be used to back a priority queue #61
Merged
Merged
Changes from all commits
Commits
Show all changes
63 commits
Select commit
Hold shift + click to select a range
91b7e50
Add PriorityQueue implementation built on top of a MinMaxHeap
AquaGeek f81c588
Merge MinMaxHeap into PriorityQueue
AquaGeek c42096c
Add `unordered` read-only view into underlying heap
AquaGeek 107a9b4
Start filling in PriorityQueue benchmarks
AquaGeek db87e18
Implement our own swapAt()
AquaGeek 101bc2c
Rename _delete(at:) to _remove(at:)
AquaGeek 1171c0a
Rename removeMin/removeMax -> popMin/popMax
AquaGeek 11ba582
Use magic values 1, 2 to refer to the items in the first max level
AquaGeek a9efa56
Specify logarithmic complexities as "O(log `count`) / 2" instead of "…
AquaGeek 919c019
Clarify logic in popMax
AquaGeek 5cca169
Simplify logic in _remove(at:)
AquaGeek 5b80eeb
Move the bounds checking into the various index computation methods
AquaGeek 9fe1481
Floyd's heap construction algorithm should start from count/2 - 1
AquaGeek 1ceb496
Add ObjC wrapper around CFBinaryHeap to benchmarks
AquaGeek 9f319ab
Fix benchmarks that broke because of renames
AquaGeek 9182557
Add removeMin/removeMax
AquaGeek 8e92d77
Make _minMaxHeapIsMinLevel an instance method
AquaGeek 18de4fc
Split _indexOfChildOrGrandchild(of:sortedUsing:) into two separate fu…
AquaGeek 25b88e6
Defer comparing children when determining largest/smallest descendant
AquaGeek 0b46a53
Added an iterator of the min and max views to the priority queue
AmanuelEphrem daa4f94
seperated iterator implementation into a new file
AmanuelEphrem ee35cc1
Use renamed popMin/Max in Iterator instead of removeMin/Max
AquaGeek c095031
Fix code formatting
AquaGeek 6452adf
Add sequence initializer
AquaGeek 4f6471f
Fix code formatting in benchmarks
AquaGeek 309fa96
Fix benchmark names
AquaGeek edc6bcd
Remove init from Collection
AquaGeek 43d13ea
Add conformance to ExpressibleByArrayLiteral
AquaGeek feb9b7c
Move ExpressibleByArrayLiteral conformance to separate file
AquaGeek 21efcf9
Update PriorityQueue's CMakeLists.txt
AquaGeek e8501bc
Make ascending and descending iterators public
AquaGeek 6cb5842
Inline ALL THE THINGS!
AquaGeek 07f0649
Iterative instead of recursive implementation, @inline(__always) a co…
hassila fdcd604
Address PR feedback, thanks!
hassila 593eadb
Check invariants on insertion and deletion
AquaGeek 021704b
Minor code formatting cleanup
AquaGeek 88faaee
Add copyright header to test file
AquaGeek 2557ef1
Fix missing empty line
AquaGeek a4f9ba0
Add naïve implementation of insert(contentsOf:)
AquaGeek 69583d8
Add documentation on complexity of init<S:Sequence>(_:)
AquaGeek b8507fa
Cite source paper in documentation
AquaGeek cb78920
Rename PriorityQueue -> MinMaxHeap
AquaGeek f3349e5
Mark insert(contentsOf:) as inlinable
AquaGeek 2bc784a
Rename argument label "startingAt" -> "elementAt"
AquaGeek ceae6ea
Remove coefficients from complexity docs
AquaGeek 272bb02
Make `_minMaxHeapIsMinLevel` take an index instead of a count
AquaGeek d1c529e
Rename MinMaxHeap -> Heap
AquaGeek d7bf55d
Add documentation for Heap
AquaGeek 60b6ab2
Make Heap.Iterator init and direction internal
AquaGeek c37395c
Fix reference to queue in documentation
AquaGeek 23dbd4f
Don't wrap integers in CFBinaryHeap benchmark in NSNumber
AquaGeek 7b633fd
Avoid heap allocation altogether
AquaGeek b7a0f7f
Add table with performance of operations
AquaGeek 55538df
Add heap performance graph
AquaGeek 55b5500
Apply suggestions from code review
AquaGeek 563c75e
Make _checkInvariants comments a doc comment
AquaGeek 42fbc80
Split Heap.Iterator into two separate views
AquaGeek 32358a9
Prefix heap storage variable with an underscore
AquaGeek 7d1044e
Update benchmark image
AquaGeek bd1f007
Fix Sources/PriorityQueueModule/CMakeLists.txt
AquaGeek c6a7954
CFBinaryHeap is only available on Darwin
lorentey 4bc94ac
CFBinaryHeap is only available on Darwin
lorentey 083102e
CFBinaryHeap is only available on Darwin
lorentey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FIXME: I wonder if
objc_msg_send
introduces measurable overhead in the benchmarks -- it's fast, but I don't expect it fares well when compared to a direct function call, and these microbenchmarks are tiny enough that even minute overheads have an effect. It would be interesting to compare results to a variant where these are all C functions.(This doesn't need to be resolved before this lands.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I started writing a C++ benchmark for comparison but haven't been able to finish that yet. I'd be happy for any assistance improving the comparative benchmarks here from anyone; I'm quickly approaching the limits of my understanding of perf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can have a go at it relatively soon.