ns-2 calendar code
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; -*- */
|
||||
/*
|
||||
* XXXX
|
||||
*/
|
||||
|
||||
#include "ns2-calendar-scheduler.h"
|
||||
#include "event-impl.h"
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "ns3/assert.h"
|
||||
#include "ns3/log.h"
|
||||
#include <cassert>
|
||||
#include <math.h>
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
@@ -17,54 +16,465 @@ NS_LOG_COMPONENT_DEFINE ("Ns2CalendarScheduler");
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (Ns2CalendarScheduler);
|
||||
|
||||
#define CALENDAR_HASH(key) ((key.m_ts / width_) % nbuckets_)
|
||||
|
||||
TypeId
|
||||
Ns2CalendarScheduler::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::Ns2CalendarScheduler")
|
||||
.SetParent<Scheduler> ()
|
||||
.AddConstructor<Ns2CalendarScheduler> ()
|
||||
;
|
||||
return tid;
|
||||
static TypeId tid = TypeId ("ns3::Ns2CalendarScheduler")
|
||||
.SetParent<Scheduler> ()
|
||||
.AddConstructor<Ns2CalendarScheduler> ()
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
Ns2CalendarScheduler::Ns2CalendarScheduler ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
adjust_new_width_interval_ = 10;
|
||||
min_bin_width_ = 1;
|
||||
avg_gap_ = -2;
|
||||
last_time_ = 0;
|
||||
gap_num_ = 0;
|
||||
head_search_ = 0;
|
||||
insert_search_ = 0;
|
||||
round_num_ = 0;
|
||||
time_to_newwidth_ = adjust_new_width_interval_;
|
||||
cal_clock_ = Scheduler::EventKey ();
|
||||
reinit(4, 1.0, cal_clock_);
|
||||
}
|
||||
Ns2CalendarScheduler::~Ns2CalendarScheduler ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
for (int i = 0; i < nbuckets_; i++) {
|
||||
Bucket *bucket = &buckets_[i];
|
||||
if (bucket->list_ == 0) {
|
||||
continue;
|
||||
}
|
||||
if (bucket->list_->next_ == bucket->list_) {
|
||||
delete bucket->list_;
|
||||
continue;
|
||||
}
|
||||
BucketItem *next;
|
||||
BucketItem *cur;
|
||||
for (cur = bucket->list_;
|
||||
cur->next_ != bucket->list_;
|
||||
cur = next) {
|
||||
next = cur->next_;
|
||||
delete cur;
|
||||
}
|
||||
delete cur;
|
||||
}
|
||||
delete [] buckets_;
|
||||
qsize_ = 0;
|
||||
stat_qsize_ = 0;
|
||||
}
|
||||
void
|
||||
Ns2CalendarScheduler::Insert (const Event &ev)
|
||||
Ns2CalendarScheduler::Insert (const Event &event)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
Scheduler::EventKey newtime = event.key;
|
||||
int i = CALENDAR_HASH(newtime);
|
||||
|
||||
Bucket* current=(&buckets_[i]);
|
||||
BucketItem *head = current->list_;
|
||||
BucketItem *after=0;
|
||||
BucketItem *e = new BucketItem ();
|
||||
e->event = event;
|
||||
|
||||
if (!head) {
|
||||
current->list_ = e;
|
||||
e->next_ = e->prev_ = e;
|
||||
++stat_qsize_;
|
||||
++(current->count_);
|
||||
} else {
|
||||
insert_search_++;
|
||||
if (newtime < head->event.key) {
|
||||
// e-> head -> ...
|
||||
e->next_ = head;
|
||||
e->prev_ = head->prev_;
|
||||
e->prev_->next_ = e;
|
||||
head->prev_ = e;
|
||||
current->list_ = e;
|
||||
++stat_qsize_;
|
||||
++(current->count_);
|
||||
} else {
|
||||
for (after = head->prev_; newtime < after->event.key; after = after->prev_)
|
||||
{ insert_search_++; };
|
||||
//...-> after -> e -> ...
|
||||
e->next_ = after->next_;
|
||||
e->prev_ = after;
|
||||
e->next_->prev_ = e;
|
||||
after->next_ = e;
|
||||
if (after->event.key < newtime) {
|
||||
//unique timing
|
||||
++stat_qsize_;
|
||||
++(current->count_);
|
||||
}
|
||||
}
|
||||
}
|
||||
++qsize_;
|
||||
//assert(e == buckets_[i].list_ || e->prev_->time_ <= e->time_);
|
||||
//assert(e == buckets_[i].list_->prev_ || e->next_->time_ >= e->time_);
|
||||
|
||||
if (stat_qsize_ > top_threshold_) {
|
||||
resize(nbuckets_ << 1, cal_clock_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bool
|
||||
Ns2CalendarScheduler::IsEmpty (void) const
|
||||
{
|
||||
return false;
|
||||
return qsize_ == 0;
|
||||
}
|
||||
Scheduler::Event
|
||||
Ns2CalendarScheduler::PeekNext (void) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return Scheduler::Event ();
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_ASSERT (!IsEmpty ());
|
||||
|
||||
BucketItem *e = const_cast<Ns2CalendarScheduler *> (this)->head ();
|
||||
NS_ASSERT (e != 0);
|
||||
|
||||
return e->event;
|
||||
}
|
||||
|
||||
|
||||
Scheduler::Event
|
||||
Ns2CalendarScheduler::RemoveNext (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return Scheduler::Event ();
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_ASSERT (!IsEmpty ());
|
||||
|
||||
BucketItem *e = head ();
|
||||
NS_ASSERT (e != 0);
|
||||
|
||||
if (last_time_ == 0) last_time_ = e->event.key.m_ts;
|
||||
else
|
||||
{
|
||||
gap_num_ ++;
|
||||
if (gap_num_ >= qsize_ ) {
|
||||
uint64_t tt_gap_ = e->event.key.m_ts - last_time_;
|
||||
avg_gap_ = tt_gap_ / gap_num_;
|
||||
gap_num_ = 0;
|
||||
last_time_ = e->event.key.m_ts;
|
||||
round_num_ ++;
|
||||
if ((round_num_ > 20) &&
|
||||
(( head_search_> (insert_search_<<1))
|
||||
||( insert_search_> (head_search_<<1)) ))
|
||||
{
|
||||
resize(nbuckets_, cal_clock_);
|
||||
round_num_ = 0;
|
||||
} else {
|
||||
if (round_num_ > 100) {
|
||||
round_num_ = 0;
|
||||
head_search_ = 0;
|
||||
insert_search_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int l = lastbucket_;
|
||||
|
||||
// update stats and unlink
|
||||
if (e->next_ == e || e->next_->event.key != e->event.key) {
|
||||
--stat_qsize_;
|
||||
//assert(stat_qsize_ >= 0);
|
||||
--buckets_[l].count_;
|
||||
//assert(buckets_[l].count_ >= 0);
|
||||
}
|
||||
--qsize_;
|
||||
|
||||
if (e->next_ == e)
|
||||
buckets_[l].list_ = 0;
|
||||
else {
|
||||
e->next_->prev_ = e->prev_;
|
||||
e->prev_->next_ = e->next_;
|
||||
buckets_[l].list_ = e->next_;
|
||||
}
|
||||
|
||||
e->next_ = e->prev_ = NULL;
|
||||
|
||||
|
||||
//if (buckets_[l].count_ == 0)
|
||||
// assert(buckets_[l].list_ == 0);
|
||||
|
||||
if (stat_qsize_ < bot_threshold_) {
|
||||
resize(nbuckets_ >> 1, cal_clock_);
|
||||
}
|
||||
|
||||
Scheduler::Event event = e->event;
|
||||
delete e;
|
||||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
Ns2CalendarScheduler::Remove (const Event &ev)
|
||||
{
|
||||
NS_ASSERT (!IsEmpty ());
|
||||
NS_ASSERT (false);
|
||||
NS_ASSERT (!IsEmpty ());
|
||||
|
||||
|
||||
int i = CALENDAR_HASH(ev.key);
|
||||
|
||||
Bucket* current=(&buckets_[i]);
|
||||
BucketItem *head = current->list_;
|
||||
BucketItem *e=0;
|
||||
|
||||
if (!head) {
|
||||
NS_LOG_DEBUG ("event not in scheduler");
|
||||
return;
|
||||
}
|
||||
for (e = head->prev_; ev.key != e->event.key; e = e->prev_) {}
|
||||
--stat_qsize_;
|
||||
--buckets_[i].count_;
|
||||
if (e->next_ == e) {
|
||||
assert(buckets_[i].list_ == e);
|
||||
buckets_[i].list_ = 0;
|
||||
} else {
|
||||
e->next_->prev_ = e->prev_;
|
||||
e->prev_->next_ = e->next_;
|
||||
if (buckets_[i].list_ == e)
|
||||
buckets_[i].list_ = e->next_;
|
||||
}
|
||||
|
||||
if (buckets_[i].count_ == 0)
|
||||
assert(buckets_[i].list_ == 0);
|
||||
|
||||
e->next_ = e->prev_ = NULL;
|
||||
|
||||
delete e;
|
||||
|
||||
--qsize_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Ns2CalendarScheduler::BucketItem *
|
||||
Ns2CalendarScheduler::head (void)
|
||||
{
|
||||
NS_ASSERT (!IsEmpty ());
|
||||
|
||||
int l = -1, i = lastbucket_;
|
||||
int lastbucket_dec = (lastbucket_) ? lastbucket_ - 1 : nbuckets_ - 1;
|
||||
uint64_t diff;
|
||||
BucketItem *e, *min_e = NULL;
|
||||
#define CAL_DEQUEUE(x) \
|
||||
do { \
|
||||
head_search_++; \
|
||||
if ((e = buckets_[i].list_) != NULL) { \
|
||||
diff = e->event.key.m_ts - cal_clock_.m_ts; \
|
||||
if (diff < diff##x##_) { \
|
||||
l = i; \
|
||||
goto found_l; \
|
||||
} \
|
||||
if (min_e == NULL || min_e->event.key > e->event.key) { \
|
||||
min_e = e; \
|
||||
l = i; \
|
||||
} \
|
||||
} \
|
||||
if (++i == nbuckets_) i = 0; \
|
||||
} while (0)
|
||||
|
||||
// CAL_DEQUEUE applied successively will find the event to
|
||||
// dequeue (within one year) and keep track of the
|
||||
// minimum-priority event seen so far; the argument controls
|
||||
// the criteria used to decide whether the event should be
|
||||
// considered `within one year'. Importantly, the number of
|
||||
// buckets should not be less than 4.
|
||||
CAL_DEQUEUE(0);
|
||||
CAL_DEQUEUE(1);
|
||||
for (; i != lastbucket_dec; ) {
|
||||
CAL_DEQUEUE(2);
|
||||
}
|
||||
// one last bucket is left unchecked - take the minimum
|
||||
// [could have done CAL_DEQUEUE(3) with diff3_ = bwidth*(nbuck*3/2-1)]
|
||||
e = buckets_[i].list_;
|
||||
if (min_e != NULL &&
|
||||
(e == NULL || min_e->event.key < e->event.key))
|
||||
e = min_e;
|
||||
else {
|
||||
//assert(e);
|
||||
l = i;
|
||||
}
|
||||
found_l:
|
||||
//assert(buckets_[l].count_ >= 0);
|
||||
//assert(buckets_[l].list_ == e);
|
||||
|
||||
/* l is the index of the bucket to dequeue, e is the event */
|
||||
/*
|
||||
* still want to advance lastbucket_ and cal_clock_ to save
|
||||
* time when deque() follows.
|
||||
*/
|
||||
assert (l != -1);
|
||||
lastbucket_ = l;
|
||||
cal_clock_ = e->event.key;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void
|
||||
Ns2CalendarScheduler::reinit(int nbuck, uint64_t bwidth, Scheduler::EventKey start)
|
||||
{
|
||||
buckets_ = new Bucket[nbuck];
|
||||
|
||||
memset(buckets_, 0, sizeof(Bucket)*nbuck); //faster than ctor
|
||||
|
||||
width_ = bwidth;
|
||||
nbuckets_ = nbuck;
|
||||
qsize_ = 0;
|
||||
stat_qsize_ = 0;
|
||||
|
||||
lastbucket_ = CALENDAR_HASH(start);
|
||||
|
||||
diff0_ = bwidth*nbuck/2;
|
||||
diff1_ = diff0_ + bwidth;
|
||||
diff2_ = bwidth*nbuck;
|
||||
//diff3_ = bwidth*(nbuck*3/2-1);
|
||||
|
||||
bot_threshold_ = (nbuck >> 1) - 2;
|
||||
top_threshold_ = (nbuck << 1);
|
||||
}
|
||||
|
||||
void
|
||||
Ns2CalendarScheduler::resize(int newsize, Scheduler::EventKey start)
|
||||
{
|
||||
double bwidth;
|
||||
if (newsize == nbuckets_) {
|
||||
/* we resize for bwidth*/
|
||||
if (head_search_) bwidth = head_search_; else bwidth = 1;
|
||||
if (insert_search_) bwidth = bwidth / insert_search_;
|
||||
bwidth = sqrt (bwidth) * width_;
|
||||
if (bwidth < min_bin_width_) {
|
||||
if (time_to_newwidth_>0) {
|
||||
time_to_newwidth_ --;
|
||||
head_search_ = 0;
|
||||
insert_search_ = 0;
|
||||
round_num_ = 0;
|
||||
return; //failed to adjust bwidth
|
||||
} else {
|
||||
// We have many (adjust_new_width_interval_) times failure in adjusting bwidth.
|
||||
// should do a reshuffle with newwidth
|
||||
bwidth = newwidth(newsize);
|
||||
}
|
||||
}
|
||||
//snoopy queue calculation
|
||||
} else {
|
||||
/* we resize for size */
|
||||
bwidth = newwidth(newsize);
|
||||
if (newsize < 4)
|
||||
newsize = 4;
|
||||
}
|
||||
|
||||
Bucket *oldb = buckets_;
|
||||
int oldn = nbuckets_;
|
||||
|
||||
reinit(newsize, bwidth, start);
|
||||
|
||||
// copy events to new buckets
|
||||
int i;
|
||||
|
||||
for (i = 0; i < oldn; ++i) {
|
||||
// we can do inserts faster, if we use insert2, but to
|
||||
// preserve FIFO, we have to start from the end of
|
||||
// each bucket and use insert2
|
||||
if (oldb[i].list_) {
|
||||
BucketItem *tail = oldb[i].list_->prev_;
|
||||
BucketItem *e = tail;
|
||||
do {
|
||||
BucketItem* ep = e->prev_;
|
||||
e->next_ = e->prev_ = 0;
|
||||
insert2(e);
|
||||
e = ep;
|
||||
} while (e != tail);
|
||||
}
|
||||
}
|
||||
head_search_ = 0;
|
||||
insert_search_ = 0;
|
||||
round_num_ = 0;
|
||||
delete [] oldb;
|
||||
}
|
||||
|
||||
void
|
||||
Ns2CalendarScheduler::insert2(Ns2CalendarScheduler::BucketItem* e)
|
||||
{
|
||||
// Same as insert, but for inserts e *before* any same-time-events, if
|
||||
// there should be any. Since it is used only by CalendarScheduler::newwidth(),
|
||||
// some important checks present in insert() need not be performed.
|
||||
|
||||
int i = CALENDAR_HASH(e->event.key);
|
||||
BucketItem *head = buckets_[i].list_;
|
||||
BucketItem *before=0;
|
||||
if (!head) {
|
||||
buckets_[i].list_ = e;
|
||||
e->next_ = e->prev_ = e;
|
||||
++stat_qsize_;
|
||||
++buckets_[i].count_;
|
||||
} else {
|
||||
bool newhead;
|
||||
if (e->event.key > head->prev_->event.key) { //strict LIFO, so > and not >=
|
||||
// insert at the tail
|
||||
before = head;
|
||||
newhead = false;
|
||||
} else {
|
||||
// insert event in time sorted order, LIFO for sim-time events
|
||||
for (before = head; e->event.key > before->event.key; before = before->next_)
|
||||
;
|
||||
newhead = (before == head);
|
||||
}
|
||||
|
||||
e->next_ = before;
|
||||
e->prev_ = before->prev_;
|
||||
before->prev_ = e;
|
||||
e->prev_->next_ = e;
|
||||
if (newhead) {
|
||||
buckets_[i].list_ = e;
|
||||
//assert(e->time_ <= e->next_->time_);
|
||||
}
|
||||
|
||||
if (e != e->next_ && e->next_->event.key != e->event.key) {
|
||||
// unique timing
|
||||
++stat_qsize_;
|
||||
++buckets_[i].count_;
|
||||
}
|
||||
}
|
||||
//assert(e == buckets_[i].list_ || e->prev_->time_ <= e->time_);
|
||||
//assert(e == buckets_[i].list_->prev_ || e->next_->time_ >= e->time_);
|
||||
|
||||
++qsize_;
|
||||
// no need to check resizing
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Ns2CalendarScheduler::newwidth(int newsize)
|
||||
{
|
||||
if (adjust_new_width_interval_) {
|
||||
time_to_newwidth_ = adjust_new_width_interval_;
|
||||
if (avg_gap_ > 0) return avg_gap_*4.0;
|
||||
}
|
||||
int i;
|
||||
int max_bucket = 0; // index of the fullest bucket
|
||||
for (i = 1; i < nbuckets_; ++i) {
|
||||
if (buckets_[i].count_ > buckets_[max_bucket].count_)
|
||||
max_bucket = i;
|
||||
}
|
||||
int nsamples = buckets_[max_bucket].count_;
|
||||
|
||||
if (nsamples <= 4) return width_;
|
||||
|
||||
uint64_t nw = buckets_[max_bucket].list_->prev_->event.key.m_ts
|
||||
- buckets_[max_bucket].list_->event.key.m_ts;
|
||||
|
||||
nw /= ((newsize < nsamples) ? newsize : nsamples); // min (newsize, nsamples)
|
||||
nw *= 4.0;
|
||||
|
||||
nw = std::max (nw, min_bin_width_);
|
||||
|
||||
return nw;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 1994 Regents of the University of California.
|
||||
* Copyright (c) 2009 INRIA
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation;
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Authors:
|
||||
* David Wetherall <djw@juniper.lcs.mit.edu>: originally, in ns-2, back in 1997,
|
||||
* David Wetherall <djw@juniper.lcs.mit.edu>: originally, in ns-2, back in 1997
|
||||
* David X. Wei: optimizations in ns-2.28
|
||||
* Mathieu Lacage <mathieu.lacage@sophia.inria.fr>: port to ns-3
|
||||
*/
|
||||
|
||||
@@ -53,6 +38,44 @@ public:
|
||||
virtual void Remove (const Event &ev);
|
||||
|
||||
private:
|
||||
struct BucketItem {
|
||||
ns3::Scheduler::Event event;
|
||||
struct BucketItem *next_;
|
||||
struct BucketItem *prev_;
|
||||
};
|
||||
struct Bucket {
|
||||
struct BucketItem *list_;
|
||||
int count_;
|
||||
};
|
||||
|
||||
void reinit(int nbuck, uint64_t bwidth, Scheduler::EventKey start);
|
||||
void resize(int newsize, Scheduler::EventKey start);
|
||||
uint64_t newwidth(int newsize);
|
||||
void insert2 (Ns2CalendarScheduler::BucketItem *e);
|
||||
Ns2CalendarScheduler::BucketItem *head (void);
|
||||
|
||||
|
||||
uint64_t min_bin_width_; // minimum bin width for Calendar Queue
|
||||
unsigned int adjust_new_width_interval_; // The interval (in unit of resize time) for adjustment of bin width. A zero value disables automatic bin width adjustment
|
||||
unsigned time_to_newwidth_; // how many time we failed to adjust the width based on snoopy-queue
|
||||
long unsigned head_search_;
|
||||
long unsigned insert_search_;
|
||||
int round_num_;
|
||||
long int gap_num_; //the number of gap samples in this window (in process of calculation)
|
||||
uint64_t last_time_; //the departure time of first event in this window
|
||||
double avg_gap_; //the average gap in last window (finished calculation)
|
||||
|
||||
uint64_t width_;
|
||||
uint64_t diff0_, diff1_, diff2_; /* wrap-around checks */
|
||||
|
||||
int stat_qsize_; /* # of distinct priorities in queue*/
|
||||
int nbuckets_;
|
||||
int lastbucket_;
|
||||
int top_threshold_;
|
||||
int bot_threshold_;
|
||||
int qsize_;
|
||||
struct Bucket *buckets_;
|
||||
Scheduler::EventKey cal_clock_;
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
Reference in New Issue
Block a user