ns-2 calendar code
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 15 Jan 2009 20:56:06 +0100
changeset 4096 4542370485ef
parent 4095 4b919c166ec3
child 4097 e0fc1999118c
ns-2 calendar code
src/simulator/ns2-calendar-scheduler.cc
src/simulator/ns2-calendar-scheduler.h
--- 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 <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_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
--- 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 <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 @@
   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