A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
calendar-scheduler.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 INRIA
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
7 * Author: Alexander Krotov <krotov@iitp.ru>
8 */
9
10#include "calendar-scheduler.h"
11
12#include "assert.h"
13#include "boolean.h"
14#include "log.h"
15#include "type-id.h"
16
17#include <list>
18#include <string>
19
20/**
21 * @file
22 * @ingroup scheduler
23 * ns3::CalendarScheduler class implementation.
24 */
25
26namespace ns3
27{
28
29NS_LOG_COMPONENT_DEFINE("CalendarScheduler");
30
32
35{
36 static TypeId tid = TypeId("ns3::CalendarScheduler")
38 .SetGroupName("Core")
39 .AddConstructor<CalendarScheduler>()
40 .AddAttribute("Reverse",
41 "Store events in reverse chronological order",
43 BooleanValue(false),
46 return tid;
47}
48
50{
51 NS_LOG_FUNCTION(this);
52 Init(2, 1, 0);
53 m_qSize = 0;
54}
55
57{
58 NS_LOG_FUNCTION(this);
59 delete[] m_buckets;
60 m_buckets = nullptr;
61}
62
63void
65{
66 NS_LOG_FUNCTION(this << reverse);
67 m_reverse = reverse;
68
69 if (m_reverse)
70 {
71 NextEvent = [](Bucket& bucket) -> Scheduler::Event& { return bucket.back(); };
72 Order = [](const EventKey& a, const EventKey& b) -> bool { return a > b; };
73 Pop = [](Bucket& bucket) -> void { bucket.pop_back(); };
74 }
75 else
76 {
77 NextEvent = [](Bucket& bucket) -> Scheduler::Event& { return bucket.front(); };
78 Order = [](const EventKey& a, const EventKey& b) -> bool { return a < b; };
79 Pop = [](Bucket& bucket) -> void { bucket.pop_front(); };
80 }
81}
82
83void
84CalendarScheduler::Init(uint32_t nBuckets, uint64_t width, uint64_t startPrio)
85{
86 NS_LOG_FUNCTION(this << nBuckets << width << startPrio);
87 m_buckets = new Bucket[nBuckets];
88 m_nBuckets = nBuckets;
89 m_width = width;
90 m_lastPrio = startPrio;
91 m_lastBucket = Hash(startPrio);
92 m_bucketTop = (startPrio / width + 1) * width;
93}
94
95void
97{
98 NS_LOG_FUNCTION(this);
99
100 std::cout << "nBuckets=" << m_nBuckets << ", width=" << m_width << std::endl;
101 std::cout << "Bucket Distribution ";
102 for (uint32_t i = 0; i < m_nBuckets; i++)
103 {
104 std::cout << m_buckets[i].size() << " ";
105 }
106 std::cout << std::endl;
107}
108
110CalendarScheduler::Hash(uint64_t ts) const
111{
112 NS_LOG_FUNCTION(this);
113
114 uint32_t bucket = (ts / m_width) % m_nBuckets;
115 return bucket;
116}
117
118void
120{
121 NS_LOG_FUNCTION(this << ev.key.m_ts << ev.key.m_uid);
122 // calculate bucket index.
123 uint32_t bucket = Hash(ev.key.m_ts);
124 NS_LOG_LOGIC("insert in bucket=" << bucket);
125
126 // insert in bucket list.
127 auto end = m_buckets[bucket].end();
128 for (auto i = m_buckets[bucket].begin(); i != end; ++i)
129 {
130 if (Order(ev.key, i->key))
131 {
132 m_buckets[bucket].insert(i, ev);
133 return;
134 }
135 }
136 m_buckets[bucket].push_back(ev);
137}
138
139void
141{
142 NS_LOG_FUNCTION(this << &ev);
143 DoInsert(ev);
144 m_qSize++;
145 ResizeUp();
146}
147
148bool
150{
151 NS_LOG_FUNCTION(this);
152 return m_qSize == 0;
153}
154
157{
158 NS_LOG_FUNCTION(this);
159 NS_ASSERT(!IsEmpty());
161 uint64_t bucketTop = m_bucketTop;
162 Scheduler::Event minEvent;
163 minEvent.impl = nullptr;
164 minEvent.key.m_ts = UINT64_MAX;
165 minEvent.key.m_uid = UINT32_MAX;
166 minEvent.key.m_context = 0;
167 do
168 {
169 if (!m_buckets[i].empty())
170 {
172 if (next.key.m_ts < bucketTop)
173 {
174 return next;
175 }
176 if (next.key < minEvent.key)
177 {
178 minEvent = next;
179 }
180 }
181 i++;
182 i %= m_nBuckets;
183 bucketTop += m_width;
184 } while (i != m_lastBucket);
185
186 return minEvent;
187}
188
191{
192 NS_LOG_FUNCTION(this);
193
195 uint64_t bucketTop = m_bucketTop;
196 int32_t minBucket = -1;
197 Scheduler::EventKey minKey;
198 NS_ASSERT(!IsEmpty());
199 minKey.m_ts = uint64_t(-int64_t(1));
200 minKey.m_uid = 0;
201 minKey.m_context = 0xffffffff;
202 do
203 {
204 if (!m_buckets[i].empty())
205 {
207 if (next.key.m_ts < bucketTop)
208 {
209 m_lastBucket = i;
210 m_lastPrio = next.key.m_ts;
211 m_bucketTop = bucketTop;
212 Pop(m_buckets[i]);
213 return next;
214 }
215 if (next.key < minKey)
216 {
217 minKey = next.key;
218 minBucket = i;
219 }
220 }
221 i++;
222 i %= m_nBuckets;
223 bucketTop += m_width;
224 } while (i != m_lastBucket);
225
226 m_lastPrio = minKey.m_ts;
227 m_lastBucket = Hash(minKey.m_ts);
228 m_bucketTop = (minKey.m_ts / m_width + 1) * m_width;
229 Scheduler::Event next = NextEvent(m_buckets[minBucket]);
230 Pop(m_buckets[minBucket]);
231
232 return next;
233}
234
237{
239 NS_ASSERT(!IsEmpty());
240
242 NS_LOG_LOGIC("remove ts=" << ev.key.m_ts << ", key=" << ev.key.m_uid
243 << ", from bucket=" << m_lastBucket);
244 m_qSize--;
245 ResizeDown();
246 return ev;
247}
248
249void
251{
252 NS_LOG_FUNCTION(this << &ev);
253 NS_ASSERT(!IsEmpty());
254 // bucket index of event
255 uint32_t bucket = Hash(ev.key.m_ts);
256
257 auto end = m_buckets[bucket].end();
258 for (auto i = m_buckets[bucket].begin(); i != end; ++i)
259 {
260 if (i->key.m_uid == ev.key.m_uid)
261 {
262 NS_ASSERT(ev.impl == i->impl);
263 m_buckets[bucket].erase(i);
264
265 m_qSize--;
266 ResizeDown();
267 return;
268 }
269 }
270 NS_ASSERT(false);
271}
272
273void
275{
276 NS_LOG_FUNCTION(this);
277
278 if (m_qSize > m_nBuckets * 2 && m_nBuckets < 32768)
279 {
280 Resize(m_nBuckets * 2);
281 }
282}
283
284void
286{
287 NS_LOG_FUNCTION(this);
288
289 if (m_qSize < m_nBuckets / 2)
290 {
291 Resize(m_nBuckets / 2);
292 }
293}
294
295uint64_t
297{
298 NS_LOG_FUNCTION(this);
299
300 if (m_qSize < 2)
301 {
302 return 1;
303 }
304 uint32_t nSamples;
305 if (m_qSize <= 5)
306 {
307 nSamples = m_qSize;
308 }
309 else
310 {
311 nSamples = 5 + m_qSize / 10;
312 }
313 if (nSamples > 25)
314 {
315 nSamples = 25;
316 }
317
318 // we gather the first nSamples from the queue
319 std::list<Scheduler::Event> samples;
320 // save state
321 uint32_t lastBucket = m_lastBucket;
322 uint64_t bucketTop = m_bucketTop;
323 uint64_t lastPrio = m_lastPrio;
324
325 // gather requested events
326 for (uint32_t i = 0; i < nSamples; i++)
327 {
328 samples.push_back(DoRemoveNext());
329 }
330 // put them back
331 for (auto i = samples.begin(); i != samples.end(); ++i)
332 {
333 DoInsert(*i);
334 }
335
336 // restore state.
337 m_lastBucket = lastBucket;
338 m_bucketTop = bucketTop;
339 m_lastPrio = lastPrio;
340
341 // finally calculate inter-time average over samples.
342 uint64_t totalSeparation = 0;
343 auto end = samples.end();
344 auto cur = samples.begin();
345 auto next = cur;
346 next++;
347 while (next != end)
348 {
349 totalSeparation += next->key.m_ts - cur->key.m_ts;
350 cur++;
351 next++;
352 }
353 uint64_t twiceAvg = totalSeparation / (nSamples - 1) * 2;
354 totalSeparation = 0;
355 cur = samples.begin();
356 next = cur;
357 next++;
358 while (next != end)
359 {
360 uint64_t diff = next->key.m_ts - cur->key.m_ts;
361 if (diff <= twiceAvg)
362 {
363 totalSeparation += diff;
364 }
365 cur++;
366 next++;
367 }
368
369 totalSeparation *= 3;
370 totalSeparation = std::max(totalSeparation, (uint64_t)1);
371 return totalSeparation;
372}
373
374void
375CalendarScheduler::DoResize(uint32_t newSize, uint64_t newWidth)
376{
377 NS_LOG_FUNCTION(this << newSize << newWidth);
378
379 Bucket* oldBuckets = m_buckets;
380 uint32_t oldNBuckets = m_nBuckets;
381 Init(newSize, newWidth, m_lastPrio);
382
383 for (uint32_t i = 0; i < oldNBuckets; i++)
384 {
385 auto end = oldBuckets[i].end();
386 for (auto j = oldBuckets[i].begin(); j != end; ++j)
387 {
388 DoInsert(*j);
389 }
390 }
391 delete[] oldBuckets;
392}
393
394void
396{
397 NS_LOG_FUNCTION(this << newSize);
398
399 // PrintInfo ();
400 uint64_t newWidth = CalculateNewWidth();
401 DoResize(newSize, newWidth);
402}
403
404} // namespace ns3
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
ns3::BooleanValue attribute value declarations.
ns3::CalendarScheduler class declaration.
AttributeValue implementation for Boolean.
Definition boolean.h:26
a calendar queue event scheduler
bool m_reverse
Bucket ordering.
uint64_t CalculateNewWidth()
Compute the new bucket size, based on up to the first 25 entries.
uint32_t m_nBuckets
Number of buckets in the array.
bool(* Order)(const EventKey &newEvent, const EventKey &it)
Ordering function to identify the insertion point, according to m_reverse.
void PrintInfo()
Print the configuration and bucket size distribution.
uint32_t m_qSize
Number of events in queue.
Scheduler::Event &(* NextEvent)(Bucket &bucket)
Get the next event from the bucket, according to m_reverse.
void Remove(const Scheduler::Event &ev) override
Remove a specific event from the event list.
void Init(uint32_t nBuckets, uint64_t width, uint64_t startPrio)
Initialize the calendar queue.
void ResizeDown()
Halve the number of buckets if necessary.
uint32_t Hash(uint64_t key) const
Hash the dimensionless time to a bucket.
void Insert(const Scheduler::Event &ev) override
Insert a new Event in the schedule.
void DoInsert(const Scheduler::Event &ev)
Insert a new event in to the correct bucket.
uint64_t m_bucketTop
Priority at the top of the bucket from which last event was dequeued.
std::list< Scheduler::Event > Bucket
Calendar bucket type: a list of Events.
~CalendarScheduler() override
Destructor.
void DoResize(uint32_t newSize, uint64_t newWidth)
Resize the number of buckets and width.
Scheduler::Event PeekNext() const override
Get a pointer to the next event.
Scheduler::Event RemoveNext() override
Remove the earliest event from the event list.
Scheduler::Event DoRemoveNext()
Remove the earliest event.
uint32_t m_lastBucket
Bucket index from which the last event was dequeued.
void SetReverse(bool reverse)
Set the insertion order.
void Resize(uint32_t newSize)
Resize to a new number of buckets, with automatically computed width.
bool IsEmpty() const override
Test if the schedule is empty.
uint64_t m_lastPrio
The priority of the last event removed.
uint64_t m_width
Duration of a bucket, in dimensionless time units.
void ResizeUp()
Double the number of buckets if necessary.
Bucket * m_buckets
Array of buckets.
void(* Pop)(Bucket &)
Pop the next event from the bucket, according to m_reverse.
static TypeId GetTypeId()
Register this type.
Maintain the event list.
Definition scheduler.h:146
a unique identifier for an interface.
Definition type-id.h:49
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition type-id.h:56
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Scheduler event.
Definition scheduler.h:173
EventKey key
Key for sorting and ordering Events.
Definition scheduler.h:175
EventImpl * impl
Pointer to the event implementation.
Definition scheduler.h:174
Structure for sorting and comparing Events.
Definition scheduler.h:159
uint32_t m_context
Event context.
Definition scheduler.h:162
uint64_t m_ts
Event time stamp.
Definition scheduler.h:160
uint32_t m_uid
Event unique id.
Definition scheduler.h:161
ns3::TypeId declaration; inline and template implementations.