Skip to content

Commit 9fdb784

Browse files
AdminAdmin
authored andcommitted
Improved event queues, added test for large queues
1 parent 2e84db8 commit 9fdb784

6 files changed

Lines changed: 346 additions & 218 deletions

File tree

JSimpleSim/src/org/simplesim/core/scheduling/AbstractBucketQueue.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ abstract class AbstractBucketQueue<E, M extends Map<Time, List<E>>> implements E
3636
private final M map;
3737

3838
/** number of total events, accessible for subclasses */
39-
int size=0;
39+
private int size=0;
4040

4141
@SuppressWarnings("serial")
4242
static class UnexpectedEmptyBucketException extends RuntimeException {
@@ -61,12 +61,15 @@ M getMap() {
6161
*/
6262
@Override
6363
public Time getTime(E event) {
64-
for (final Time time : getMap().keySet()) {
65-
final List<E> bucket=getMap().get(time);
66-
if (bucket.contains(event)) return time;
64+
for (Map.Entry<Time, List<E>> entry : getMap().entrySet()) {
65+
if (entry.getValue().contains(event)) return entry.getKey();
6766
}
6867
return null;
6968
}
69+
70+
void removeEmptyBucket(Time time) {
71+
getMap().remove(time);
72+
}
7073

7174
/*
7275
* (non-Javadoc)
@@ -75,16 +78,16 @@ public Time getTime(E event) {
7578
*/
7679
@Override
7780
public Time dequeue(E event) {
78-
for (final Time time : getMap().keySet()) {
79-
final List<E> bucket=getMap().get(time);
80-
if (bucket.contains(event)) {
81-
bucket.remove(event);
82-
if (bucket.isEmpty()) getMap().remove(time);
81+
for (Map.Entry<Time, List<E>> entry : getMap().entrySet()) {
82+
if (entry.getValue().contains(event)) {
83+
entry.getValue().remove(event);
8384
size--;
84-
return time;
85+
final Time result=entry.getKey();
86+
if (entry.getValue().isEmpty()) removeEmptyBucket(result);
87+
return result;
8588
}
8689
}
87-
return null;
90+
return null;
8891
}
8992

9093
/*
@@ -136,7 +139,7 @@ public E dequeue() {
136139
final List<E> bucket=getMap().get(min);
137140
if (bucket.isEmpty()) throw new UnexpectedEmptyBucketException();
138141
final E result=bucket.remove(bucket.size()-1);
139-
if (bucket.isEmpty()) getMap().remove(min);
142+
if (bucket.isEmpty()) removeEmptyBucket(min);
140143
size--;
141144
return result;
142145
}

JSimpleSim/src/org/simplesim/core/scheduling/HashedBucketQueue.java

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,35 @@
44
*
55
* This software is published as open source and licensed under the terms of GNU
66
* GPLv3.
7-
*
7+
*
88
* Contributors:
99
* - Rene Kuhlemann - development and initial implementation
10-
*
10+
*
1111
*/
1212
package org.simplesim.core.scheduling;
1313

1414
import java.util.HashMap;
15-
import java.util.Iterator;
1615
import java.util.List;
1716

1817
/**
1918
* Event queue based on a simple time to bucket mapping, each bucket containing
20-
* events with equal time stamps.
19+
* events with equal time stamps. The value of the most imminent time stamp is
20+
* saved to accelerate {@code getMin()} and has to be kept in sync.
2121
* <p>
22-
* Elements of this queue type are unsorted, so {@code getMin()} always has a complexity of O(n).
23-
* <p>
24-
* Note: This queue is suitable for a global event queue, especially if there are a lot
25-
* of events with the same time stamp. This implementation has a smaller memory
26-
* footprint than the {@code SortedBucketQueue} and {@code HeapBucketQueue}, but all time-based look up
27-
* operations (including {@code getMin()}, {@code dequeue()} and
28-
* {@code dequeAll()}) are slower.
22+
* Note: This queue is suitable for a global event queue, especially if there
23+
* are a lot of events with the same time stamp. This implementation has a
24+
* smaller memory footprint than the {@code SortedBucketQueue} and
25+
* {@code HeapBucketQueue}, but on all dequeue operation, the new minimal time
26+
* stamp has to be found with a complexity of O(n).
2927
*
3028
* @param <E> event type
31-
*
29+
*
3230
* @see SortedBucketQueue
3331
* @see HeapBucketQueue
3432
*/
35-
public final class HashedBucketQueue<E> extends AbstractBucketQueue<E,HashMap<Time,List<E>>> {
33+
public final class HashedBucketQueue<E> extends AbstractBucketQueue<E, HashMap<Time, List<E>>> {
34+
35+
private Time minTime=Time.INFINITY;
3636

3737
public HashedBucketQueue() {
3838
super(new HashMap<>());
@@ -41,17 +41,48 @@ public HashedBucketQueue() {
4141
/*
4242
* (non-Javadoc)
4343
*
44-
* @see org.simplesim.core.scheduling.EventQueue#getMin()
44+
* @see org.simplesim.core.scheduling.EventQueue#enqueue(java.lang.Object,
45+
* org.simplesim.core.scheduling.Time)
4546
*/
4647
@Override
47-
public Time getMin() {
48-
final Iterator<Time> iterator=getMap().keySet().iterator();
49-
Time min=iterator.next();
50-
while (iterator.hasNext()) {
51-
final Time time=iterator.next();
52-
if (time.compareTo(min)<0) min=time;
48+
public void enqueue(E event, Time time) {
49+
if (time.getTicks()<minTime.getTicks()) minTime=time;
50+
super.enqueue(event,time);
51+
}
52+
53+
@Override
54+
void removeEmptyBucket(Time time) {
55+
super.removeEmptyBucket(time);
56+
if (time.equals(minTime)) minTime=findNewMinTime();
57+
}
58+
59+
/*
60+
* (non-Javadoc)
61+
*
62+
* @see org.simplesim.core.scheduling.EventQueue#dequeueAll(org.simplesim.core.
63+
* scheduling.Time)
64+
*/
65+
@Override
66+
public List<E> dequeueAll(Time time) {
67+
final List<E> result=super.dequeueAll(time);
68+
if (time.equals(minTime)) minTime=findNewMinTime();
69+
return result;
70+
}
71+
72+
private Time findNewMinTime() {
73+
Time result=Time.INFINITY;
74+
if (!getMap().isEmpty()) for (Time time : getMap().keySet()) {
75+
if (time.getTicks()<result.getTicks()) result=time;
5376
}
54-
return min;
77+
return result;
5578
}
5679

80+
/*
81+
* (non-Javadoc)
82+
*
83+
* @see org.simplesim.core.scheduling.EventQueue#getMin()
84+
*/
85+
@Override
86+
public Time getMin() { return minTime; }
87+
5788
}

JSimpleSim/src/org/simplesim/core/scheduling/HeapBucketQueue.java

Lines changed: 19 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
*/
1111
package org.simplesim.core.scheduling;
1212

13-
import java.util.ArrayList;
13+
import java.util.Collections;
1414
import java.util.HashMap;
15-
import java.util.Iterator;
1615
import java.util.List;
1716
import java.util.PriorityQueue;
1817
import java.util.Queue;
@@ -23,15 +22,12 @@
2322
* Just as the other bucket queues, this one is also based on a simple time to
2423
* bucket mapping, each bucket containing events with equal time stamps. In this
2524
* implementation, an additional {@code PriorityQueue} is used to accelerate
26-
* access to the minimal time stamp by {@code getMin()}. Within this queue, an
27-
* {@code EventQueueEntry} couples the time stamp with the bucket. Because of
28-
* the underlying heap structure, the look-up of the first element is faster
29-
* than in a {@code HashedBucketQueue}.
25+
* lookup of the minimal time stamp by {@code getMin()}. Thus, the queue has to
26+
* be maintained in parallel to the map and kept in sync.
3027
* <p>
31-
* Note: This queue type performs best for dequeuing <i>all</i> events
32-
* with minimal time stamp ({@link #dequeueAll()}). It is well suited for a
33-
* global event queue and has a performance similar to the
34-
* {@code SortedBucketQueue}.
28+
* Note: This queue type performs best for dequeuing <i>all</i> events with
29+
* minimal time stamp ({@link #dequeueAll()}). It is well suited for a global
30+
* event queue and has a performance similar to the {@code SortedBucketQueue}.
3531
*
3632
* @param <E> event type
3733
*
@@ -41,70 +37,30 @@
4137
public final class HeapBucketQueue<E> extends AbstractBucketQueue<E, HashMap<Time, List<E>>> {
4238

4339
// additional heap structure to facilitate getMin()
44-
private final Queue<EventQueueEntry<List<E>>> queue=new PriorityQueue<>();
40+
private final Queue<Time> queue=new PriorityQueue<>();
4541

4642
public HeapBucketQueue() {
4743
super(new HashMap<>());
4844
}
4945

50-
private Queue<EventQueueEntry<List<E>>> getQueue() {
51-
return queue;
52-
}
46+
private Queue<Time> getQueue() { return queue; }
5347

54-
/*
55-
* (non-Javadoc)
56-
*
57-
* @see org.simplesim.core.scheduling.EventQueue#dequeue(java.lang.Object)
58-
*/
5948
@Override
60-
public Time dequeue(E event) {
61-
final Iterator<EventQueueEntry<List<E>>> iterator=getQueue().iterator();
62-
while (iterator.hasNext()) {
63-
final EventQueueEntry<List<E>> entry=iterator.next();
64-
final List<E> bucket=entry.getEvent();
65-
if (bucket.contains(event)) {
66-
bucket.remove(event);
67-
if (bucket.isEmpty()) {
68-
getMap().remove(entry.getTime());
69-
getQueue().remove(entry);
70-
}
71-
size--;
72-
return entry.getTime();
73-
}
74-
}
75-
return null;
49+
void removeEmptyBucket(Time time) {
50+
super.removeEmptyBucket(time);
51+
getQueue().remove(time);
7652
}
7753

7854
/*
7955
* (non-Javadoc)
8056
*
81-
* @see org.simplesim.core.scheduling.EventQueue#dequeue()
57+
* @see org.simplesim.core.scheduling.EventQueue#enqueue(java.lang.Object,
58+
* org.simplesim.core.scheduling.Time)
8259
*/
83-
@Override
84-
public E dequeue() {
85-
if (isEmpty()) return null;
86-
final Time min=getMin();
87-
final List<E> bucket=getMap().get(min);
88-
if (bucket.isEmpty()) throw new UnexpectedEmptyBucketException();
89-
final E result=bucket.remove(bucket.size()-1);
90-
if (bucket.isEmpty()) {
91-
getMap().remove(min);
92-
getQueue().poll();
93-
}
94-
size--;
95-
return result;
96-
}
97-
9860
@Override
9961
public void enqueue(E event, Time time) {
100-
List<E> bucket=getMap().get(time);
101-
if (bucket==null) { // time stamp has not been added to queue, yet
102-
bucket=new ArrayList<>();
103-
getMap().put(time,bucket);
104-
getQueue().add(new EventQueueEntry<>(time,bucket));
105-
} // now we definitely have a valid entry queued in the heap
106-
bucket.add(event); // add the event to the bucket
107-
size++;
62+
if (!getMap().containsKey(time)) getQueue().add(time);
63+
super.enqueue(event,time);
10864
}
10965

11066
/*
@@ -116,16 +72,7 @@ public void enqueue(E event, Time time) {
11672
@Override
11773
public List<E> dequeueAll(Time time) {
11874
final List<E> bucket=super.dequeueAll(time);
119-
if (!bucket.isEmpty()) { // remove bucket also from queue
120-
final Iterator<EventQueueEntry<List<E>>> iterator=getQueue().iterator();
121-
while (iterator.hasNext()) {
122-
final EventQueueEntry<List<E>> entry=iterator.next();
123-
if (time.equals(entry.getTime())) {
124-
iterator.remove();
125-
return bucket;
126-
}
127-
}
128-
}
75+
if (!bucket.isEmpty()) getQueue().remove(time);
12976
return bucket;
13077
}
13178

@@ -136,11 +83,8 @@ public List<E> dequeueAll(Time time) {
13683
*/
13784
@Override
13885
public List<E> dequeueAll() {
139-
final List<E> bucket=getMap().remove(getMin());
140-
if (bucket.isEmpty()) throw new UnexpectedEmptyBucketException();
141-
getQueue().poll();
142-
size-=bucket.size();
143-
return bucket;
86+
if (getQueue().isEmpty()) return Collections.emptyList();
87+
return super.dequeueAll(getQueue().poll());
14488
}
14589

14690
/*
@@ -149,8 +93,6 @@ public List<E> dequeueAll() {
14993
* @see org.simplesim.core.scheduling.EventQueue#getMin()
15094
*/
15195
@Override
152-
public Time getMin() {
153-
return getQueue().peek().getTime();
154-
}
96+
public Time getMin() { return getQueue().peek(); }
15597

15698
}

0 commit comments

Comments
 (0)