Logo AND Algorithmique Numérique Distribuée

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