Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Remove one of the many pimple: HostModel::p_cpuModel
[simgrid.git] / src / surf / ns3 / red-queue.cc
1 /* Copyright (c) 2011, 2014. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /*
8  * Copyright (c) 2010 Regents of the University of California
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation;
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * Author: Duy Nguyen<duy@soe.ucsc.edu>
24  *
25  */
26
27 #include "ns3/enum.h"
28 #include "ns3/uinteger.h"
29 #include "ns3/double.h"
30 #include "red-queue.h"
31 #include "ns3/simulator.h"
32 #include "ns3/nstime.h"
33
34 #include <cstdlib>
35
36 #define RED_STATS_TABLE_SIZE 256
37 #define RED_STATS_MASK (RED_STATS_TABLE_SIZE - 1)
38
39 namespace ns3 {
40
41 NS_OBJECT_ENSURE_REGISTERED (RedQueue);
42
43 TypeId RedQueue::GetTypeId (void)
44 {
45   ///< Note: these parameters must be worked out beforehand for RED to work correctly
46   ///< How these parameters are set up can affect RED performance greatly
47   static TypeId tid = TypeId ("ns3::RedQueue")
48                       .SetParent<Queue> ()
49                       .AddConstructor<RedQueue> ()
50                       .AddAttribute ("Mode",
51                                      "Whether to use Bytes (see MaxBytes) or Packets (see MaxPackets) as the maximum queue size metric.",
52                                      EnumValue (BYTES), ///> currently supports BYTES only
53                                      MakeEnumAccessor (&RedQueue::SetMode),
54                                      MakeEnumChecker (BYTES, "Bytes",
55                                                       PACKETS, "Packets"))
56                       .AddAttribute ("MaxPackets",
57                                      "The maximum number of packets accepted by this RedQueue.",
58                                      UintegerValue (100),
59                                      MakeUintegerAccessor (&RedQueue::m_maxPackets),
60                                      MakeUintegerChecker<uint32_t> ())
61                       .AddAttribute ("MaxBytes",
62                                      "The maximum number of bytes accepted by this RedQueue.",
63                                      UintegerValue (100000),
64                                      MakeUintegerAccessor (&RedQueue::m_maxBytes),
65                                      MakeUintegerChecker<uint32_t> ())
66                       .AddAttribute ("m_burst",
67                                      "maximum number of m_burst packets accepted by this queue",
68                                      UintegerValue (6), ///> bursts must be > minTh/avpkt
69                                      MakeUintegerAccessor (&RedQueue::m_burst),
70                                      MakeUintegerChecker<uint32_t> ())
71                       .AddAttribute ("m_avPkt",
72                                      "In bytes, use with m_burst to determine the time constant for average queue size calculations",
73                                      UintegerValue (1024), ///> average packet size
74                                      MakeUintegerAccessor (&RedQueue::m_avPkt),
75                                      MakeUintegerChecker<uint32_t> ())
76                       .AddAttribute ("m_minTh",
77                                      "Average queue size at which marking becomes a m_prob",
78                                      UintegerValue (5120), ///> in bytes  1024x5
79                                      MakeUintegerAccessor (&RedQueue::m_minTh),
80                                      MakeUintegerChecker<uint32_t> ())
81                       .AddAttribute ("m_maxTh",
82                                      "Maximal marking m_prob, should be at least twice min to prevent synchronous retransmits",
83                                      UintegerValue (15360), ///> in bytes 1024x15
84                                      MakeUintegerAccessor (&RedQueue::m_maxTh),
85                                      MakeUintegerChecker<uint32_t> ())
86                       .AddAttribute ("m_rate",
87                                      "this m_rate is used for calculating the average queue size after some idle time.",
88                                      UintegerValue (1500000), ///> in bps, should be set to bandwidth of interface
89                                      MakeUintegerAccessor (&RedQueue::m_rate),
90                                      MakeUintegerChecker<uint64_t> ())
91                       .AddAttribute ("m_prob",
92                                      "Probability for marking, suggested values are 0.01 and 0.02",
93                                      DoubleValue (0.02),
94                                      MakeDoubleAccessor (&RedQueue::m_prob),
95                                      MakeDoubleChecker <double> ())
96   ;
97
98   return tid;
99 }
100
101 RedQueue::RedQueue ()
102   : Queue (),
103     m_packets (),
104     m_bytesInQueue (0),
105     m_wLog (0),
106     m_pLog (0),
107     m_rmask (0),
108     m_scellLog (0),
109     m_scellMax (0),
110     m_count (-1),
111     m_randNum (0),
112     m_qavg (0),
113     m_initialized (false)
114 {
115
116   m_sTable = Uint32tVector (RED_STATS_TABLE_SIZE);
117
118 }
119
120 RedQueue::~RedQueue ()
121 {
122 }
123
124 void
125 RedQueue::SetMode (enum Mode mode)
126 {
127   m_mode = mode;
128 }
129
130 RedQueue::Mode
131 RedQueue::GetMode (void)
132 {
133   return m_mode;
134 }
135
136 uint64_t
137 RedQueue::GetAverageQueueSize (void)
138 {
139   return m_qavg;
140 }
141
142
143 /**
144  * The paper says:
145  * Given minimum threshold min_th and that we wish to allow bursts of L packets
146  * Then Wq should be chosen to satisfy avg_L < min_th
147  * L + 1 + [(1-Wq)^(L+1) - 1]/ Wq    <  min_th
148  * L + 1 - min_th < [1 - (1-Wq)^(L+1)]/Wq
149  * i.e. given min_th 5, L=50, necessary that Wq <= 0.0042
150  *
151  * Hence
152  * burst + 1 - minTh/avPkt < (1-(1-W)^burst)/W
153  * this low-pass filter is used to calculate the avg queue size
154  *
155  */
156 uint32_t
157 RedQueue::evalEwma (uint32_t minTh, uint32_t burst, uint32_t avpkt)
158 {
159   uint32_t wlog = 1;
160
161
162   ///< Just a random W
163   double W = 0.5;
164
165   double temp = 0;
166
167   ///< Note: bursts must be larger than minTh/avpkt for it to work
168   temp = (double)burst + 1 - (double)minTh / avpkt;
169
170
171   if (temp < 1.0)
172     {
173       return -1;
174     }
175
176   /**
177    * wlog =1 , W = .5
178    * wlog =2 , W = .25
179    * wlog =3 , W = .125
180    * wlog =4 , W = .0625
181    * wlog =5 , W = .03125
182    * wlog =6 , W = .015625
183    * wlog =7 , W = .0078125
184    * wlog =8 , W = .00390625
185    * wlog =9 , W = .001953125
186    * wlog =10, W = .0009765625
187    */
188   for (wlog = 1; wlog < 32; wlog++, W /= 2)
189     {
190       if (temp <= (1 - pow (1 - W, burst)) / W )
191         {
192           return wlog;
193         }
194     }
195
196   return -1;
197 }
198
199 /**
200  *
201  * Plog = log (prob / (maxTh -minTh) );
202  *
203  * Paper says: When a packet arrives at the gateway and the average queue size
204  * is between min_th and max_th, the initial packet marking probability is:
205  * Pb = C1*avg - C2
206  * where,
207  * C1 = maxP/(max_th - mint_th)
208  * C2 = maxP*min_th/(max_th - mint_th)
209  * maxP could be chosen so that C1 a power of two
210  */
211 uint32_t
212 RedQueue::evalP (uint32_t minTh, uint32_t maxTh, double prob)
213 {
214
215   uint32_t i = maxTh - minTh ;
216
217   if (i <= 0)
218     {
219       return -1;
220     }
221
222   prob /= i;
223
224   ///< It returns the index that makes C1 a power of two
225   for (i = 0; i < 32; i++)
226     {
227       if (prob > 1.0)
228         {
229           break;
230         }
231       prob *= 2;
232     }
233
234   ///< Error checking
235   if (i >= 32 )
236     {
237       //NS_LOG_DEBUG ("i >= 32, this shouldn't happen");
238       return -1;
239     }
240
241   //NS_LOG_DEBUG ("\t i(makes C1 power of two)=" << i);
242   return i;
243 }
244
245
246 /**
247  * avg = avg*(1-W)^m  where m = t/xmitTime
248  *
249  * m_sTable[ t/2^cellLog] = -log(1-W) * t/xmitTime
250  * m_sTable[ t >> cellLog]= -log(1-W) * t/xmitTime
251  *
252  * t is converted to t/2^cellLog for storage in the table
253  * find out what is cellLog and return it
254  *
255  */
256 uint32_t
257 RedQueue::evalIdleDamping (uint32_t wLog, uint32_t avpkt, uint32_t bps)
258 {
259
260   ///> in microsecond ticks: 1 sec = 1000000 microsecond ticks
261   double xmitTime =  ((double) avpkt / bps) * 1000000;
262
263   ///> -log(1 - 1/2^wLog) / xmitTime
264   ///> note W = 1/2^wLog
265   double wLogTemp = -log (1.0 - 1.0 / (1 << wLog)) / xmitTime;
266
267
268   ///> the maximum allow idle time
269   double maxTime = 31 / wLogTemp;
270
271   //NS_LOG_DEBUG ("\t xmitTime=" << xmitTime << " wLogTemp=" << wLogTemp
272   //                             << " maxTime=" << maxTime);
273
274
275   uint32_t cLog, i;
276
277   for (cLog = 0; cLog < 32; cLog++)
278     {
279
280       ///> maxTime < 512* 2^cLog
281       ///>  finds the cLog that's able to cover this maxTime
282       if (maxTime / (1 << cLog) < 512)
283         {
284           break;
285         }
286
287     }
288   if (cLog >= 32)
289     {
290       return -1;
291     }
292
293   m_sTable[0] = 0;
294
295   for (i = 1; i < 255; i++)
296     {
297       ///> wLogTemp * i * 2^cLog
298       m_sTable[i] = (i << cLog) * wLogTemp;
299
300
301       if (m_sTable[i] > 31)
302         {
303           m_sTable[i] = 31;
304         }
305     }
306
307   m_sTable[255] = 31;
308
309   //NS_LOG_DEBUG ("\t cLog=" << cLog);
310   return cLog;
311 }
312
313
314 ///> red random mask
315 uint32_t
316 RedQueue::Rmask (uint32_t pLog)
317 {
318   ///> ~OUL creates a 32 bit mask
319   ///> 2^Plog - 1
320   return pLog < 32 ? ((1 << pLog) - 1) : (uint32_t) ~0UL;
321
322 }
323
324
325 void
326 RedQueue::SetParams (uint32_t minTh, uint32_t maxTh,
327                      uint32_t wLog, uint32_t pLog, uint64_t scellLog)
328 {
329
330   m_qavg = 0;
331   m_count = -1;
332   m_minTh = minTh;
333   m_maxTh = maxTh;
334   m_wLog = wLog;
335   m_pLog = pLog;
336   m_rmask = Rmask (pLog);
337   m_scellLog = scellLog;
338   m_scellMax = (255 << m_scellLog);
339
340   //NS_LOG_DEBUG ("\t m_wLog" << m_wLog << " m_pLog" << m_pLog << " m_scellLog" << m_scellLog
341   //                          << " m_minTh" << m_minTh << " m_maxTh" << m_maxTh
342   //                          << " rmask=" << m_rmask << " m_scellMax=" << m_scellMax);
343 }
344
345 int
346 RedQueue::IsIdling ()
347 {
348   //use IsZero instead
349   if ( m_idleStart.GetNanoSeconds () != 0)
350     {
351       //NS_LOG_DEBUG ("\t IsIdling");
352     }
353
354   return m_idleStart.GetNanoSeconds () != 0;
355 }
356 void
357 RedQueue::StartIdlePeriod ()
358 {
359   m_idleStart = Simulator::Now ();
360 }
361 void
362 RedQueue::EndIdlePeriod ()
363 {
364   m_idleStart = NanoSeconds (0);
365 }
366 void
367 RedQueue::Restart ()
368 {
369
370   EndIdlePeriod ();
371   m_qavg = 0;
372   m_count = -1;
373
374 }
375
376 /**
377  * m = idletime / s
378  *
379  * m  is the number of pkts that might have been transmitted by the gateway
380  * during the time that the queue was free
381  * s is a typical transmission for a packet
382  *
383  * m = idletime / (average pkt size / bandwidth)
384  *
385  * avg = avg *(1-W)^m
386  *
387  * We need to precompute a table for this calculation because of the exp power
388  *
389  */
390 uint64_t
391 RedQueue::AvgFromIdleTime ()
392 {
393   uint64_t idleTime;
394   int shift;
395
396   idleTime = ns3::Time(Simulator::Now() - m_idleStart).GetMicroSeconds();
397   //idleTime = RedTimeToInteger (Simulator::Now() - m_idleStart, Time::US);
398
399   if (idleTime > m_scellMax)
400     {
401       idleTime = m_scellMax;
402     }
403
404   //NS_LOG_DEBUG ("\t idleTime=" << idleTime);
405   //PrintTable ();
406
407   shift = m_sTable [(idleTime >>  m_scellLog) & RED_STATS_MASK];
408
409   if (shift)
410     {
411       //std::cout << "shift " << m_qavg << "=>" << (m_qavg >> shift) << std::endl;
412       return m_qavg >> shift;
413     }
414   else
415     {
416       idleTime = (m_qavg * idleTime) >> m_scellLog;
417
418
419      // NS_LOG_DEBUG ("\t idleus=" << idleTime);
420
421       if (idleTime < (m_qavg / 2))
422         {
423           //std::cout <<"idleus " <<  m_qavg << " - " << idleus << " = " << (m_qavg-idleus) << std::endl;
424           return m_qavg - idleTime;
425         }
426       else
427         {
428           //std:: cout <<"half " << m_qavg << "=>" <<  (m_qavg/2) << std::endl;
429           return (m_qavg / 2) ;
430         }
431     }
432 }
433
434 uint64_t
435 RedQueue::AvgFromNonIdleTime (uint32_t backlog)
436 {
437   //NS_LOG_FUNCTION (this << backlog);
438
439   //NS_LOG_DEBUG ("qavg " << m_qavg);
440   //NS_LOG_DEBUG ("backlog" << backlog);
441
442  /**
443   * This is basically EWMA
444   * m_qavg = q_avg*(1-W) + backlog*W
445   * m_qavg = q_avg + W(backlog - q_avg)
446   *
447   */
448   return m_qavg + (backlog - (m_qavg >> m_wLog));
449 }
450
451 uint64_t
452 RedQueue::AvgCalc (uint32_t backlog)
453 {
454   //NS_LOG_FUNCTION (this << backlog);
455
456   uint64_t qtemp;
457
458   if ( !IsIdling ())
459     {
460       qtemp = AvgFromNonIdleTime (backlog);
461       //NS_LOG_DEBUG ("NonIdle Avg " << qtemp);
462       //std::cout <<"n "<< qtemp << std::endl;
463       return qtemp;
464     }
465   else
466     {
467       qtemp = AvgFromIdleTime ();
468       //NS_LOG_DEBUG ("Idle Avg" << qtemp);
469       //std::cout <<"i "<< qtemp << std::endl;
470       return qtemp;
471     }
472 }
473
474 int
475 RedQueue::CheckThresh (uint64_t avg)
476 {
477
478   //NS_LOG_FUNCTION (this << avg);
479   //NS_LOG_DEBUG ("\t check threshold: min " << m_minTh << " max" << m_maxTh);
480
481   if (avg < m_minTh)
482     {
483       return BELOW_MIN_THRESH;
484     }
485   else if (avg >= m_maxTh)
486     {
487       return ABOVE_MAX_THRESH;
488     }
489   else
490     {
491       return BETWEEN_THRESH;
492     }
493 }
494 uint32_t
495 RedQueue::RedRandom ()
496 {
497   //NS_LOG_FUNCTION_NOARGS ();
498
499   ///> obtain a random u32 number
500   ///> return m_rmask & ran.GetInteger ();
501   //checkme
502   return m_rmask & rand ();
503 }
504 int
505 RedQueue::MarkProbability (uint64_t avg)
506 {
507   //NS_LOG_FUNCTION (this << avg);
508   //NS_LOG_DEBUG ("\t m_randNum " << m_randNum);
509   //NS_LOG_DEBUG ("\t right\t" << m_randNum);
510   //NS_LOG_DEBUG ("\t left\t" << ((avg - m_minTh)*m_count));
511
512   ///> max_P* (qavg - qth_min)/(qth_max-qth_min) < rnd/qcount
513   //return !((avg - m_minTh ) * m_count < m_randNum);
514   //checkme
515   return !((avg - m_minTh )* m_count < m_randNum);
516
517 }
518 int
519 RedQueue::Processing (uint64_t qavg)
520 {
521
522   //NS_LOG_FUNCTION (this << "qavg" << qavg << " m_minTh" << m_minTh << " m_maxTh" << m_maxTh);
523
524   switch (CheckThresh (qavg))
525     {
526     case BELOW_MIN_THRESH:
527       //NS_LOG_DEBUG ("\t below threshold ");
528
529       m_count = -1;
530       return DONT_MARK;
531
532     case BETWEEN_THRESH:
533       //NS_LOG_DEBUG ("\t between threshold ");
534
535       if (++m_count)
536         {
537           //NS_LOG_DEBUG ("\t check Mark Prob");
538           if (MarkProbability (qavg))
539             {
540               m_count = 0;
541               m_randNum = RedRandom ();
542
543               //NS_LOG_DEBUG ("\t Marked Will Drop " << m_qavg);
544
545               return PROB_MARK;
546             }
547           //NS_LOG_DEBUG ("\t Marked Will Save " << m_qavg);
548         }
549       else
550         {
551           m_randNum = RedRandom ();
552         }
553       return DONT_MARK;
554
555     case ABOVE_MAX_THRESH:
556
557       //NS_LOG_DEBUG ("\t above threshold ");
558
559       m_count = -1;
560       return HARD_MARK;
561     }
562
563   //NS_LOG_DEBUG ("BUG HERE\n");
564   return DONT_MARK;
565 }
566
567
568 bool
569 RedQueue::DoEnqueue (Ptr<Packet> p)
570 {
571   //NS_LOG_FUNCTION (this << p);
572
573   if (m_mode == PACKETS && (m_packets.size () >= m_maxPackets))
574     {
575       //NS_LOG_LOGIC ("Queue full (at max packets) -- droppping pkt");
576       Drop (p);
577       return false;
578     }
579
580   if (m_mode == BYTES && (m_bytesInQueue + p->GetSize () >= m_maxBytes))
581     {
582       //NS_LOG_LOGIC ("Queue full (packet would exceed max bytes) -- droppping pkt");
583       Drop (p);
584       return false;
585     }
586
587   if (!m_initialized)
588     {
589       // making sure all the variables are initialized ok
590       //NS_LOG_DEBUG ("\t m_maxPackets" << m_maxPackets
591       //                                << " m_maxBytes" << m_maxBytes
592       //                                << " m_burst" << m_burst << " m_avPkt" << m_avPkt
593       //                                << " m_minTh" << m_minTh << " m_maxTh" << m_maxTh
594       //                                << " m_rate" << m_rate <<  " m_prob" << m_prob);
595
596       m_wLog = evalEwma (m_minTh, m_burst, m_avPkt);
597       m_pLog = evalP (m_minTh, m_maxTh, m_prob);
598       m_scellLog = evalIdleDamping (m_wLog, m_avPkt, m_rate);
599
600       SetParams (m_minTh, m_maxTh, m_wLog, m_pLog, m_scellLog);
601       EndIdlePeriod ();
602 //      srand((unsigned)time(0));
603       m_initialized = true;
604     }
605
606 //  PrintTable();
607
608   if (GetMode () == BYTES)
609     {
610       m_qavg = AvgCalc (m_bytesInQueue);
611     }
612   else if (GetMode () == PACKETS)
613     {
614       //not yet supported
615 //      m_qavg = AvgCalc (m_packets.size ());
616     }
617
618   //NS_LOG_DEBUG ("\t bytesInQueue  " << m_bytesInQueue << "\tQavg " << m_qavg);
619   //NS_LOG_DEBUG ("\t packetsInQueue  " << m_packets.size () << "\tQavg " << m_qavg);
620
621
622   if (IsIdling ())
623     {
624       EndIdlePeriod ();
625     }
626
627   switch (Processing (m_qavg) )
628     {
629     case DONT_MARK:
630       break;
631
632     case PROB_MARK:
633       //NS_LOG_DEBUG ("\t Dropping due to Prob Mark " << m_qavg);
634       m_stats.probDrop++;
635       m_stats.probMark++;
636       Drop (p);
637       return false;
638
639     case HARD_MARK:
640       //NS_LOG_DEBUG ("\t Dropping due to Hard Mark " << m_qavg);
641       m_stats.forcedMark++;
642       m_stats.probDrop++;
643       Drop (p);
644       return false;
645     }
646
647
648   m_bytesInQueue += p->GetSize ();
649   m_packets.push_back (p);
650
651   //NS_LOG_LOGIC ("Number packets " << m_packets.size ());
652   //NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
653
654   return true;
655 }
656
657 Ptr<Packet>
658 RedQueue::DoDequeue (void)
659 {
660   //NS_LOG_FUNCTION (this);
661
662   if (m_packets.empty ())
663     {
664       //NS_LOG_LOGIC ("Queue empty");
665       return 0;
666     }
667
668   Ptr<Packet> p = m_packets.front ();
669   m_packets.pop_front ();
670   m_bytesInQueue -= p->GetSize ();
671
672   //NS_LOG_LOGIC ("Popped " << p);
673
674   //NS_LOG_LOGIC ("Number packets " << m_packets.size ());
675   //NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
676
677   if (m_bytesInQueue <= 0 && !IsIdling ())
678     {
679       StartIdlePeriod ();
680     }
681
682   return p;
683 }
684
685 ///> just for completeness
686 /// m_packets.remove (p) also works
687 int
688 RedQueue::DropPacket (Ptr<Packet> p)
689 {
690
691   //NS_LOG_FUNCTION (this << p);
692
693   //NS_LOG_DEBUG ("\t Dropping Packet p");
694
695   std::list<Ptr<Packet> >::iterator iter;
696   uint32_t packetSize;
697
698   for (iter = m_packets.begin(); iter != m_packets.end(); ++iter)
699     {
700       if (*iter == p)
701         {
702           packetSize= p->GetSize ();
703           m_packets.erase(iter);
704           m_bytesInQueue -= packetSize;
705           return 1;
706         }
707     }
708
709   if (!IsIdling ())
710     {
711       StartIdlePeriod ();
712     }
713
714   return 0;
715 }
716
717 Ptr<const Packet>
718 RedQueue::DoPeek (void) const
719 {
720   //NS_LOG_FUNCTION (this);
721
722   if (m_packets.empty ())
723     {
724       //NS_LOG_LOGIC ("Queue empty");
725       return NULL;
726     }
727
728   Ptr<Packet> p = m_packets.front ();
729
730   //NS_LOG_LOGIC ("Number packets " << m_packets.size ());
731   //NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
732
733   return p;
734 }
735
736 void
737 RedQueue::PrintTable ()
738 {
739   //NS_LOG_FUNCTION_NOARGS ();
740
741   for (uint32_t i = 0; i < RED_STATS_TABLE_SIZE; i++)
742     {
743       std::cout << m_sTable[i] << " ";
744     }
745   std::cout << std::endl;
746 }
747
748
749 } // namespace ns3