RESOLVED FIXED Bug 170579
Add a PriorityQueue class
https://bugs.webkit.org/show_bug.cgi?id=170579
Summary Add a PriorityQueue class
Keith Miller
Reported 2017-04-06 17:41:03 PDT
Add PriorityQueue class
Attachments
Patch (35.79 KB, patch)
2017-04-06 17:54 PDT, Keith Miller
no flags
Patch (35.82 KB, patch)
2017-04-06 18:26 PDT, Keith Miller
no flags
Patch (37.44 KB, patch)
2017-04-07 12:56 PDT, Keith Miller
no flags
Patch (39.52 KB, patch)
2017-04-07 14:40 PDT, Keith Miller
no flags
Patch for landing (39.51 KB, patch)
2017-04-07 14:40 PDT, Keith Miller
no flags
Patch for landing (40.03 KB, patch)
2017-04-07 15:01 PDT, Keith Miller
no flags
Patch for landing (39.99 KB, patch)
2017-04-07 17:35 PDT, Keith Miller
no flags
Keith Miller
Comment 1 2017-04-06 17:54:29 PDT
Yusuke Suzuki
Comment 2 2017-04-06 18:16:27 PDT
Comment on attachment 306450 [details] Patch I think we can use std::push_heap & std::pop_heap to simplify the implementation. What do you think of?
Keith Miller
Comment 3 2017-04-06 18:26:34 PDT
Keith Miller
Comment 4 2017-04-06 18:46:37 PDT
(In reply to Yusuke Suzuki from comment #2) > Comment on attachment 306450 [details] > Patch > > I think we can use std::push_heap & std::pop_heap to simplify the > implementation. What do you think of? I'm not sure that really helps since we need to have a siftUp and siftDown for the increaseKey/decreaseKey methods.
Yusuke Suzuki
Comment 5 2017-04-06 19:57:51 PDT
(In reply to Keith Miller from comment #4) > (In reply to Yusuke Suzuki from comment #2) > > Comment on attachment 306450 [details] > > Patch > > > > I think we can use std::push_heap & std::pop_heap to simplify the > > implementation. What do you think of? > > I'm not sure that really helps since we need to have a siftUp and siftDown > for the increaseKey/decreaseKey methods. Seems right. While increaseKey can be implemented in push_heap, decreaseKey requires siftDown which is not directly offered by stl.
Saam Barati
Comment 6 2017-04-07 00:39:01 PDT
Comment on attachment 306453 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=306453&action=review PriorityQueue LGTM, just one thing looks weird to me about how you're using this in Plan. Comments below. > Source/WTF/ChangeLog:19 > + The only way to update the location of an entry is to do a hash > + table lookup with the entry's hash but this is probably more > + expensive than just doing a linear search. This seems unlikely to be true if the heap gets large. Perhaps it's worth filing a bug to investigate this if it ever becomes an issue? > Source/JavaScriptCore/wasm/WasmWorklist.cpp:112 > + worklist.m_queue.enqueue(WTFMove(element)); > worklist.m_planEnqueued->notifyAll(locker); > + element = worklist.m_queue.peek(); I'm confused as to what this is doing? What if you get back a different element when you do this? Actually, this also looks like a potential problem in the old queue as well, except that it'd naturally handle it since it never WTFMove() the element, so in the worst case, you'd notify all your threads and they'd grab a bunch of new plans and validate them, instead of compiling the element's plan in parallel. > Source/WTF/wtf/PriorityQueue.h:39 > +// Style: remove empty "//" > Source/WTF/wtf/PriorityQueue.h:54 > + void enqueue(T&& element) > + { > + size_t location = m_buffer.size(); > + m_buffer.append(WTFMove(element)); > + siftUp(location); > + } You should use forwarding semantics here. > Source/WTF/wtf/PriorityQueue.h:106 > + size_t parentOf(size_t location) const { ASSERT(location); return (location - 1) / 2; } > + size_t leftChildOf(size_t location) const { return location * 2 + 1; } > + size_t rightChildOf(size_t location) const { return leftChildOf(location) + 1; } Style nit: I'd make these static constexpr > Source/WTF/wtf/PriorityQueue.h:138 > + IsHigherPriority isHigherPriority; It's questionable that you need to instantiate a value here. Why not just have an API here that requires some static function called isHigherPriority(a, b)? > Tools/TestWebKitAPI/Tests/WTF/PriorityQueue.cpp:34 > +constexpr std::size_t operator "" _z ( unsigned long long n ) { return n; } Is this needed? You only use it in 2 places. > Tools/TestWebKitAPI/Tests/WTF/PriorityQueue.cpp:196 > +} It might also be worth having a test based off randomness, but for values being enqueued/dequeued, and total length of the test.
Keith Miller
Comment 7 2017-04-07 12:23:59 PDT
Comment on attachment 306453 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=306453&action=review >> Source/WTF/ChangeLog:19 >> + expensive than just doing a linear search. > > This seems unlikely to be true if the heap gets large. Perhaps it's worth filing a bug to investigate this if it ever becomes an issue? Sounds good. I didn't mention this but it's also pretty rare that we actually use the increase/decreaseKey functions so the cost of a O(n) is pretty rare. Also, the hash cost applies to enqueue and dequeue as well so changing the value of the key would likely need to be very common to outweigh the cost of searching. >> Source/JavaScriptCore/wasm/WasmWorklist.cpp:112 >> + element = worklist.m_queue.peek(); > > I'm confused as to what this is doing? What if you get back a different element when you do this? > > Actually, this also looks like a potential problem in the old queue as well, except that it'd naturally handle it since it never WTFMove() the element, so in the worst case, you'd notify all your threads and they'd grab a bunch of new plans and validate them, instead of compiling the element's plan in parallel. I think this works but only because compilation is strictly higher priority than validation. It might be conceptually simpler to poll again instead, however. I'll make that change. >> Source/WTF/wtf/PriorityQueue.h:39 >> +// > > Style: remove empty "//" Fixed. >> Source/WTF/wtf/PriorityQueue.h:54 >> + } > > You should use forwarding semantics here. Fixed. >> Source/WTF/wtf/PriorityQueue.h:106 >> + size_t rightChildOf(size_t location) const { return leftChildOf(location) + 1; } > > Style nit: I'd make these static constexpr Changed. >> Source/WTF/wtf/PriorityQueue.h:138 >> + IsHigherPriority isHigherPriority; > > It's questionable that you need to instantiate a value here. Why not just have an API here that requires some static function called isHigherPriority(a, b)? Otherwise, you can't use std::less and friends as the comparator. If you think it's worth it we could implement our own template isLess, isGreater, etc. >> Tools/TestWebKitAPI/Tests/WTF/PriorityQueue.cpp:34 >> +constexpr std::size_t operator "" _z ( unsigned long long n ) { return n; } > > Is this needed? You only use it in 2 places. Either way. I can replace it with a const size_t zero. >> Tools/TestWebKitAPI/Tests/WTF/PriorityQueue.cpp:196 >> +} > > It might also be worth having a test based off randomness, but for values being enqueued/dequeued, and total length of the test. Sounds good.
Keith Miller
Comment 8 2017-04-07 12:56:10 PDT
Saam Barati
Comment 9 2017-04-07 13:02:21 PDT
Comment on attachment 306530 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=306530&action=review r=me > Source/JavaScriptCore/wasm/WasmWorklist.cpp:184 > + while (!m_queue.isEmpty()) { > + QueueElement element = m_queue.dequeue(); > if (&element.plan->vm() == &vm) { > element.plan->cancel(); > - return IterateResult::DropAndContinue; > + continue; > } You need to rebase. This is no longer the code. > Source/WTF/wtf/PriorityQueue.h:39 > +// Please remove this. > Source/WTF/wtf/PriorityQueue.h:138 > + IsHigherPriority isHigherPriority; I still don't quite understand why we need this, instead of something akin to HashTraits. Maybe file a bug to do this? It's unfortunate to make this class use more memory than needed. The only use of this class ATM will be a struct with no field members. It could just as easily be some abstract API.
Keith Miller
Comment 10 2017-04-07 14:40:07 PDT
Keith Miller
Comment 11 2017-04-07 14:40:35 PDT
Created attachment 306537 [details] Patch for landing
WebKit Commit Bot
Comment 12 2017-04-07 14:42:48 PDT
Comment on attachment 306537 [details] Patch for landing Rejecting attachment 306537 [details] from commit-queue. Failed to run "['/Volumes/Data/EWS/WebKit/Tools/Scripts/webkit-patch', '--status-host=webkit-queues.webkit.org', '--bot-id=webkit-cq-01', 'apply-attachment', '--no-update', '--non-interactive', 306537, '--port=mac']" exit_code: 2 cwd: /Volumes/Data/EWS/WebKit Last 500 characters of output: ScriptCore/wasm/WasmWorklist.h patching file Source/WTF/WTF.xcodeproj/project.pbxproj patching file Source/WTF/wtf/MathExtras.h patching file Source/WTF/wtf/PriorityQueue.h patching file Tools/ChangeLog Hunk #1 succeeded at 1 with fuzz 3. patching file Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj patching file Tools/TestWebKitAPI/Tests/WTF/PriorityQueue.cpp Failed to run "[u'/Volumes/Data/EWS/WebKit/Tools/Scripts/svn-apply', '--force']" exit_code: 1 cwd: /Volumes/Data/EWS/WebKit Full output: http://webkit-queues.webkit.org/results/3492593
Keith Miller
Comment 13 2017-04-07 15:01:57 PDT
Created attachment 306539 [details] Patch for landing
WebKit Commit Bot
Comment 14 2017-04-07 15:30:30 PDT
Comment on attachment 306539 [details] Patch for landing Rejecting attachment 306539 [details] from commit-queue. Failed to run "['/Volumes/Data/EWS/WebKit/Tools/Scripts/webkit-patch', '--status-host=webkit-queues.webkit.org', '--bot-id=webkit-cq-01', 'build', '--no-clean', '--no-update', '--build-style=release', '--port=mac']" exit_code: 2 cwd: /Volumes/Data/EWS/WebKit Last 500 characters of output: athExtras.dia -c /Volumes/Data/EWS/WebKit/Source/JavaScriptCore/b3/B3MathExtras.cpp -o /Volumes/Data/EWS/WebKit/WebKitBuild/JavaScriptCore.build/Release/JavaScriptCore.build/Objects-normal/x86_64/B3MathExtras.o ** BUILD FAILED ** The following build commands failed: CompileC /Volumes/Data/EWS/WebKit/WebKitBuild/JavaScriptCore.build/Release/JavaScriptCore.build/Objects-normal/x86_64/WasmWorklist.o wasm/WasmWorklist.cpp normal x86_64 c++ com.apple.compilers.llvm.clang.1_0.compiler (1 failure) Full output: http://webkit-queues.webkit.org/results/3492675
Keith Miller
Comment 15 2017-04-07 17:35:02 PDT
Created attachment 306554 [details] Patch for landing
WebKit Commit Bot
Comment 16 2017-04-07 18:15:12 PDT
Comment on attachment 306554 [details] Patch for landing Clearing flags on attachment: 306554 Committed r215135: <http://trac.webkit.org/changeset/215135>
WebKit Commit Bot
Comment 17 2017-04-07 18:15:14 PDT
All reviewed patches have been landed. Closing bug.
Csaba Osztrogonác
Comment 18 2017-04-08 01:34:53 PDT
(In reply to WebKit Commit Bot from comment #16) > Comment on attachment 306554 [details] > Patch for landing > > Clearing flags on attachment: 306554 > > Committed r215135: <http://trac.webkit.org/changeset/215135> It broke the GTK build: In file included from ../../Source/JavaScriptCore/wasm/WasmWorklist.h:35:0, from ../../Source/JavaScriptCore/wasm/WasmWorklist.cpp:26: ../../Source/WTF/wtf/PriorityQueue.h: In instantiation of ‘static constexpr size_t WTF::PriorityQueue<T, isHigherPriority, inlineCapacity>::parentOf(size_t) [with T = JSC::Wasm::Worklist::QueueElement; bool (* isHigherPriority)(const T&, const T&) = JSC::Wasm::Worklist::isHigherPriority; long unsigned int inlineCapacity = 10ul; size_t = long unsigned int]’: ../../Source/WTF/wtf/PriorityQueue.h:110:44: required from ‘void WTF::PriorityQueue<T, isHigherPriority, inlineCapacity>::siftUp(size_t) [with T = JSC::Wasm::Worklist::QueueElement; bool (* isHigherPriority)(const T&, const T&) = JSC::Wasm::Worklist::isHigherPriority; long unsigned int inlineCapacity = 10ul; size_t = long unsigned int]’ ../../Source/WTF/wtf/PriorityQueue.h:52:24: required from ‘void WTF::PriorityQueue<T, isHigherPriority, inlineCapacity>::enqueue(T) [with T = JSC::Wasm::Worklist::QueueElement; bool (* isHigherPriority)(const T&, const T&) = JSC::Wasm::Worklist::isHigherPriority; long unsigned int inlineCapacity = 10ul]’ ../../Source/JavaScriptCore/wasm/WasmWorklist.cpp:112:81: required from here ../../Source/WTF/wtf/PriorityQueue.h:103:95: error: body of constexpr function ‘static constexpr size_t WTF::PriorityQueue<T, isHigherPriority, inlineCapacity>::parentOf(size_t) [with T = JSC::Wasm::Worklist::QueueElement; bool (* isHigherPriority)(const T&, const T&) = JSC::Wasm::Worklist::isHigherPriority; long unsigned int inlineCapacity = 10ul; size_t = long unsigned int]’ not a return-statement static constexpr size_t parentOf(size_t location) { ASSERT(location); return (location - 1) / 2; } ^
Geoffrey Garen
Comment 19 2017-04-10 09:22:23 PDT
Comment on attachment 306554 [details] Patch for landing The std library has built-in heap functions. You should consider using those. (std has a priority queue as well. The only reason not to use it is that it will do the slow kind of allocation.)
Darin Adler
Comment 20 2017-04-11 09:14:22 PDT
When I needed a priority queue in the Timer.cpp implementation, I used std::push_heap and std::pop_heap. RunLoopGeneric.cpp and LegacyTileGrid.mm also use this approach.
Keith Miller
Comment 21 2017-04-11 09:47:02 PDT
(In reply to Geoffrey Garen from comment #19) > The std library has built-in heap functions. You should consider using those. (In reply to Darin Adler from comment #20) > When I needed a priority queue in the Timer.cpp implementation, I used > std::push_heap and std::pop_heap. RunLoopGeneric.cpp and LegacyTileGrid.mm > also use this approach. I considered using the std::push_heap and std::pop_heap methods but it's not possible to implement decreaseKey() with std::pop_heap and there is no officially supported std::sift_down method. Although, it is possible to implement increaseKey() with std::push_heap. (In reply to Geoffrey Garen from comment #19) > (std has a priority queue as well. The only reason not to use it is > that it will do the slow kind of allocation.) std::priority_queue does not support iterating the queue, which is important for the Wasm use case. Additionally, std::priority_queue does not support an increaseKey or decreaseKey method. While I could subclass std::priority_queue and implement those methods, if I'm going to go through that effort I might as well make a priority queue with all the bells and whistles I want.
Geoffrey Garen
Comment 22 2017-04-11 10:50:14 PDT
Since increaseKey and decreaseKey are O(n) in their implementations here, you could get similar performance using std by just changing a key and then calling make_heap again. I guess you've saved a little bit of performance since, after your O(n) search, you move the entry in O(log(n)) time, whereas make_heap would move the entry in O(n) time.
Note You need to log in before you can comment on or make changes to this bug.