Brad,
The domain resizing may represent a substantial performance hit. I
looked into using a fixed array and const domain with an internal index
on the heap size (see below); very pleasantly, this version is
comparable in speed to similar examples in C++ (and about 10-20 times
faster than my previous version). This suggests that an alternative
domain map could be very beneficial.
I have also provided a small discrete event simulation class and example
below.
Kindly, Mark.
/**
Mark Clements
2015-08-10
Apache v2.0
**/
class Heap {
type eltType;
const D : domain(1) = {0..1000}; // const
var heap : [D] eltType;
const FIRST : int = 0;
var LAST : int = -1; // that is, empty
proc left(pos : int) : int { return pos*2 + 1; }
proc right(pos : int) : int { return (pos+1)*2; }
proc parent(pos : int) : int { return (pos-1)/2; }
proc head() : eltType { return heap.head(); }
proc clear() { LAST = -1; } // current does _not_ clear values:
heap.clear() changes D
proc isEmpty() : bool { return LAST == -1; }
proc insert(x : eltType) {
// ASSERT( LAST < D.high )
LAST += 1;
heap[LAST] = x;
var k = LAST;
var k_parent : int;
while(k > FIRST) {
k_parent = parent(k);
if (heap[k] < heap[k_parent]) {
heap[k] <=> heap[k_parent];
k = k_parent;
} else {
return;
}
}
}
proc pop_first() : eltType {
// ASSERT( !isEmpty() )
var res : eltType = heap[FIRST];
if (FIRST == LAST) {
LAST -= 1;
return res;
}
heap[FIRST] = heap[LAST];
LAST -= 1;
var k = FIRST;
var k_next : int;
while(true) {
k_next = left(k);
if (k_next > LAST) {
break;
}
if (right(k) <= LAST && heap[right(k)] < heap[k_next]) {
k_next = right(k);
}
if (heap[k_next] < heap[k]) {
heap[k] <=> heap[k_next];
k = k_next;
} else {
break;
}
}
return res;
}
}
class cEvent {
var sendingTime : real;
var time : real;
proc action() { }
}
inline proc <(ev1 : cEvent, ev2 : cEvent) {
return ev1.time < ev2.time;
}
class Simulation {
var eq : Heap(cEvent) = new Heap(cEvent);
var simulationTime : real = 0.0;
var running : bool = false;
proc now() : real { return simulationTime; }
proc scheduleAt(time : real, ev : cEvent) {
ev.sendingTime = now();
ev.time = time;
eq.insert(ev);
}
proc run() {
simulationTime = 0.0;
running = true;
init();
while(!eq.isEmpty() && running) {
var ev : cEvent = eq.pop_first();
simulationTime = ev.time;
ev.action();
}
}
proc stop_simulation() { eq.clear(); running = false; }
proc init() { }
}
class Example : Simulation {
var show : bool = true;
class Tick : cEvent {
proc action() {
if (simulationTime < 5.0) {
if (show) then write("tick..");
scheduleAt(now()+1,new Tock());
} else {
if (show) then writeln("ring!");
stop_simulation();
}
}
}
class Tock : cEvent {
proc action() {
if (show) then write("tock..");
scheduleAt(now()+1,new Tick());
}
}
proc init() {
scheduleAt(1,new Tick());
}
}
var sim = new Example();
sim.run();
sim.show=false;
for i in 1..100000 {
sim.run();
}
On 07/29/2015 06:03 PM, Brad Chamberlain wrote:
> Hi Mark --
>
> I held off a day in responding because I didn't have any particularly
> insightful information and didn't want to preclude other responses by
> being the first to answer. But since nobody else has picked this up yet,
> I'll say that I'm not aware of other implementations of a heap/priority
> queue in Chapel at present. I don't believe that we ever received any code
> contributions back from the Kassel work that you mention. If you end up
> with code that you're happy with and would like to contribute back to the
> project (as part of the standard libraries, say, or even just a
> test/example code), please consider it.
>
> I think using Chapel's arrays are a reasonable way to express heaps in
> Chapel and, at a glance, like the approach you've taken below. I wanted
> to make one performance-oriented note on the use of the array's push/pop
> routines that you're using below (though you may already be aware of it),
> to note that the current default implementation of arrays doesn't amortize
> domain/array resizing as one would want/expect in cases like this. We
> have plans to provide an alternative domain map that supports amortized
> resizing for the next release to address this (and I'm sorry that we don't
> already have that ready -- I believe it's very low-hanging fruit. If it
> was of great short-term interest to you, we could look at whether it would
> be possible to bump its priority in short order).
>
> I'll note that one could also work around the lack of such a domain map by
> doing the recursive doubling/halving within your Heap class itself, but
> the amount of effort required is probably similar to (if not greater than)
> doing the work of the previous paragraph, so I'm not sure I'd suggest it
> unless you're really anxious to proceed on your own.
>
> One other note which may or may not matter for your work: At present, the
> push/pop-style operators are not task-safe, so if you were to parallelize
> operations on your heap class, you'd want to use some sort of coordination
> to avoid unsafe races.
>
> Hope this is helpful, and thanks for your interest in Chapel,
> -Brad
>
>
>
> On Tue, 28 Jul 2015, Mark Clements wrote:
>
>> Is there a standard implementation for a min-heap (or priority queue)
>> data structure in Chapel? (I want to implement simple discrete event
>> simulations in Chapel.) I noticed the interesting treatise from Kassel
>> by Marco Postigo Perez on "Distributed Priority Queue in Chapel" but
>> found no code.
>>
>> I have given a simple array-based implementation below, but I am
>> uncertain whether arrays are the best way to implement this.
>>
>> Kindly, Mark.
>>
>> // heap.chpl
>> // Mark Clements <[email protected]> 2015-07-28
>> // Apache v2.0
>>
>> class Heap {
>>
>> type eltType;
>> var D : domain(1) = {0..-1};
>> var heap : [D] eltType;
>> const FIRST : int = 0;
>>
>> proc last() : int { return heap.numElements-1; }
>> proc left(pos : int) : int { return pos*2 + 1; }
>> proc right(pos : int) : int { return (pos+1)*2; }
>> proc parent(pos : int) : int { return (pos-1)/2; }
>>
>> proc head() : eltType { return heap.head(); }
>> proc clear() { heap.clear(); }
>> proc isEmpty() : bool { return heap.isEmpty(); }
>>
>> proc insert(x : eltType) {
>> heap.push_back(x);
>> var k = last();
>> var k_parent : int;
>> while(k > FIRST) {
>> k_parent = parent(k);
>> if (heap[k] < heap[k_parent]) {
>> heap[k] <=> heap[k_parent];
>> k = k_parent;
>> } else {
>> return;
>> }
>> }
>> }
>>
>> proc pop() : eltType {
>> // assert( !heap.isEmpty())
>> var res : eltType = heap[FIRST];
>> if (FIRST == last()) {
>> heap.pop_back();
>> return res;
>> }
>> heap[FIRST] = heap[last()];
>> heap.pop_back();
>> var k = FIRST;
>> var k_next : int;
>> while(true) {
>> k_next = left(k);
>> if (k_next > last()) {
>> break;
>> }
>> if (right(k) <= last() && heap[right(k)] < heap[k_next]) {
>> k_next = right(k);
>> }
>> if (heap[k_next] < heap[k]) {
>> heap[k] <=> heap[k_next];
>> k = k_next;
>> } else {
>> break;
>> }
>> }
>> return res;
>> }
>> }
>>
>> var X = new Heap(int);
>> X.insert(2);
>> X.insert(3);
>> X.insert(1);
>> for i in 1..3 do writeln(X.pop());
>>
>> ------------------------------------------------------------------------------
>> _______________________________________________
>> Chapel-users mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/chapel-users
>>
------------------------------------------------------------------------------
_______________________________________________
Chapel-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/chapel-users