Logo AND Algorithmique Numérique Distribuée

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