# HG changeset patch # User Mathieu Lacage # Date 1232049366 -3600 # Node ID 4542370485ef3c7791280acd4a98547e359c4305 # Parent 4b919c166ec3eb23bdf1ea76a361229e5c0d77b8 ns-2 calendar code diff -r 4b919c166ec3 -r 4542370485ef src/simulator/ns2-calendar-scheduler.cc --- a/src/simulator/ns2-calendar-scheduler.cc Thu Jan 15 20:55:34 2009 +0100 +++ b/src/simulator/ns2-calendar-scheduler.cc Thu Jan 15 20:56:06 2009 +0100 @@ -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 -#include -#include #include "ns3/assert.h" #include "ns3/log.h" +#include +#include namespace ns3 { @@ -17,54 +16,465 @@ 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 () - .AddConstructor () - ; - return tid; + static TypeId tid = TypeId ("ns3::Ns2CalendarScheduler") + .SetParent () + .AddConstructor () + ; + 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 (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 diff -r 4b919c166ec3 -r 4542370485ef src/simulator/ns2-calendar-scheduler.h --- a/src/simulator/ns2-calendar-scheduler.h Thu Jan 15 20:55:34 2009 +0100 +++ b/src/simulator/ns2-calendar-scheduler.h Thu Jan 15 20:56:06 2009 +0100 @@ -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 : originally, in ns-2, back in 1997, + * David Wetherall : originally, in ns-2, back in 1997 + * David X. Wei: optimizations in ns-2.28 * Mathieu Lacage : port to ns-3 */ @@ -53,6 +38,44 @@ 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