traffic-control: Add Feng's Adaptive RED

This commit is contained in:
Sourabh Jain
2017-05-28 10:21:38 +02:00
parent 6c0840352d
commit 2732d27e75
6 changed files with 416 additions and 1 deletions

View File

@@ -0,0 +1,190 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2016 NITK Surathkal
*
* 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
*
* Author: Sourabh Jain <sourabhjain560@outlook.com>
* Mohit P. Tahiliani <tahiliani@nitk.edu.in>
*/
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/point-to-point-layout-module.h"
#include "ns3/traffic-control-module.h"
#include <iostream>
#include <iomanip>
#include <map>
using namespace ns3;
int main (int argc, char *argv[])
{
uint32_t nLeaf = 10;
uint32_t maxPackets = 100;
bool modeBytes = false;
uint32_t queueDiscLimitPackets = 1000;
double minTh = 5;
double maxTh = 15;
uint32_t pktSize = 512;
std::string appDataRate = "10Mbps";
std::string queueDiscType = "RED";
uint16_t port = 5001;
std::string bottleNeckLinkBw = "1Mbps";
std::string bottleNeckLinkDelay = "50ms";
CommandLine cmd;
cmd.AddValue ("nLeaf", "Number of left and right side leaf nodes", nLeaf);
cmd.AddValue ("maxPackets","Max Packets allowed in the device queue", maxPackets);
cmd.AddValue ("queueDiscLimitPackets","Max Packets allowed in the queue disc", queueDiscLimitPackets);
cmd.AddValue ("queueDiscType", "Set Queue disc type to RED or FengAdaptive", queueDiscType);
cmd.AddValue ("appPktSize", "Set OnOff App Packet Size", pktSize);
cmd.AddValue ("appDataRate", "Set OnOff App DataRate", appDataRate);
cmd.AddValue ("modeBytes", "Set Queue disc mode to Packets <false> or bytes <true>", modeBytes);
cmd.AddValue ("redMinTh", "RED queue minimum threshold", minTh);
cmd.AddValue ("redMaxTh", "RED queue maximum threshold", maxTh);
cmd.Parse (argc,argv);
if ((queueDiscType != "RED") && (queueDiscType != "FengAdaptive"))
{
std::cout << "Invalid queue disc type: Use --queueDiscType=RED or --queueDiscType=FengAdaptive" << std::endl;
exit (1);
}
Config::SetDefault ("ns3::OnOffApplication::PacketSize", UintegerValue (pktSize));
Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue (appDataRate));
Config::SetDefault ("ns3::QueueBase::Mode", StringValue ("QUEUE_MODE_PACKETS"));
Config::SetDefault ("ns3::QueueBase::MaxPackets", UintegerValue (maxPackets));
if (!modeBytes)
{
Config::SetDefault ("ns3::RedQueueDisc::Mode", StringValue ("QUEUE_DISC_MODE_PACKETS"));
Config::SetDefault ("ns3::RedQueueDisc::QueueLimit", UintegerValue (queueDiscLimitPackets));
}
else
{
Config::SetDefault ("ns3::RedQueueDisc::Mode", StringValue ("QUEUE_DISC_MODE_BYTES"));
Config::SetDefault ("ns3::RedQueueDisc::QueueLimit", UintegerValue (queueDiscLimitPackets * pktSize));
minTh *= pktSize;
maxTh *= pktSize;
}
Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (minTh));
Config::SetDefault ("ns3::RedQueueDisc::MaxTh", DoubleValue (maxTh));
Config::SetDefault ("ns3::RedQueueDisc::LinkBandwidth", StringValue (bottleNeckLinkBw));
Config::SetDefault ("ns3::RedQueueDisc::LinkDelay", StringValue (bottleNeckLinkDelay));
Config::SetDefault ("ns3::RedQueueDisc::MeanPktSize", UintegerValue (pktSize));
if (queueDiscType == "FengAdaptive")
{
// Turn on Feng's Adaptive RED
Config::SetDefault ("ns3::RedQueueDisc::FengAdaptive", BooleanValue (true));
}
// Create the point-to-point link helpers
PointToPointHelper bottleNeckLink;
bottleNeckLink.SetDeviceAttribute ("DataRate", StringValue (bottleNeckLinkBw));
bottleNeckLink.SetChannelAttribute ("Delay", StringValue (bottleNeckLinkDelay));
PointToPointHelper pointToPointLeaf;
pointToPointLeaf.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
pointToPointLeaf.SetChannelAttribute ("Delay", StringValue ("1ms"));
PointToPointDumbbellHelper d (nLeaf, pointToPointLeaf,
nLeaf, pointToPointLeaf,
bottleNeckLink);
// Install Stack
InternetStackHelper stack;
for (uint32_t i = 0; i < d.LeftCount (); ++i)
{
stack.Install (d.GetLeft (i));
}
for (uint32_t i = 0; i < d.RightCount (); ++i)
{
stack.Install (d.GetRight (i));
}
stack.Install (d.GetLeft ());
stack.Install (d.GetRight ());
TrafficControlHelper tchBottleneck;
QueueDiscContainer queueDiscs;
tchBottleneck.SetRootQueueDisc ("ns3::RedQueueDisc");
tchBottleneck.Install (d.GetLeft ()->GetDevice (0));
queueDiscs = tchBottleneck.Install (d.GetRight ()->GetDevice (0));
// Assign IP Addresses
d.AssignIpv4Addresses (Ipv4AddressHelper ("10.1.1.0", "255.255.255.0"),
Ipv4AddressHelper ("10.2.1.0", "255.255.255.0"),
Ipv4AddressHelper ("10.3.1.0", "255.255.255.0"));
// Install on/off app on all right side nodes
OnOffHelper clientHelper ("ns3::TcpSocketFactory", Address ());
clientHelper.SetAttribute ("OnTime", StringValue ("ns3::UniformRandomVariable[Min=0.|Max=1.]"));
clientHelper.SetAttribute ("OffTime", StringValue ("ns3::UniformRandomVariable[Min=0.|Max=1.]"));
Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", sinkLocalAddress);
ApplicationContainer sinkApps;
for (uint32_t i = 0; i < d.LeftCount (); ++i)
{
sinkApps.Add (packetSinkHelper.Install (d.GetLeft (i)));
}
sinkApps.Start (Seconds (0.0));
sinkApps.Stop (Seconds (30.0));
ApplicationContainer clientApps;
for (uint32_t i = 0; i < d.RightCount (); ++i)
{
// Create an on/off app sending packets to the left side
AddressValue remoteAddress (InetSocketAddress (d.GetLeftIpv4Address (i), port));
clientHelper.SetAttribute ("Remote", remoteAddress);
clientApps.Add (clientHelper.Install (d.GetRight (i)));
}
clientApps.Start (Seconds (1.0)); // Start 1 second after sink
clientApps.Stop (Seconds (15.0)); // Stop before the sink
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
std::cout << "Running the simulation" << std::endl;
Simulator::Run ();
RedQueueDisc::Stats st = StaticCast<RedQueueDisc> (queueDiscs.Get (0))->GetStats ();
if (st.unforcedDrop == 0)
{
std::cout << "There should be some unforced drops" << std::endl;
exit (1);
}
if (st.qLimDrop != 0)
{
std::cout << "There should be zero drops due to queue full" << std::endl;
exit (1);
}
std::cout << "*** Stats from the bottleneck queue disc ***" << std::endl;
std::cout << "\t " << st.unforcedDrop << " drops due to prob mark" << std::endl;
std::cout << "\t " << st.forcedDrop << " drops due to hard mark" << std::endl;
std::cout << "\t " << st.qLimDrop << " drops due to queue full" << std::endl;
std::cout << "Destroying the simulation" << std::endl;
Simulator::Destroy ();
return 0;
}

View File

@@ -8,3 +8,6 @@ def build(bld):
obj = bld.create_ns3_program('queue-discs-benchmark',
['internet', 'point-to-point', 'applications', 'internet-apps', 'traffic-control', 'flow-monitor'])
obj.source = 'queue-discs-benchmark.cc'
obj = bld.create_ns3_program('red-vs-fengadaptive', ['point-to-point', 'point-to-point-layout', 'internet', 'applications', 'traffic-control'])
obj.source = 'red-vs-fengadaptive.cc'

View File

@@ -35,6 +35,12 @@ in ns-3 contains implementation of both the features, and is a port of Sally
Floyd's ns-2 ARED model. Note that the user is allowed to choose and explicitly
configure the simulation by selecting feature (i) or feature (ii), or both.
Feng's Adaptive RED
===================
Feng's Adaptive RED is a variant of RED that adapts the maximum drop
probability. The model in ns-3 contains implementation of this feature, and is a
port of ns-2 Feng's Adaptive RED model.
Explicit Congestion Notification (ECN)
======================================
This RED model supports an ECN mode of operation to notify endpoints of
@@ -57,12 +63,16 @@ the ``Mark ()`` method.
References
==========
The RED queue aims to be close to the results cited in:
The RED queue disc aims to be close to the results cited in:
S.Floyd, K.Fall http://icir.org/floyd/papers/redsims.ps
ARED queue implementation is based on the algorithm provided in:
S. Floyd et al, http://www.icir.org/floyd/papers/adaptiveRed.pdf
Feng's Adaptive RED queue implementation is based on the algorithm
provided in:
W. C. Feng et al, http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=752150
The addition of explicit congestion notification (ECN) to IP:
K. K. Ramakrishnan et al, https://tools.ietf.org/html/rfc3168
@@ -99,6 +109,14 @@ In addition to RED attributes, ARED queue requires following attributes:
* Beta (decrement parameter for m_curMaxP)
* RTT
In addition to RED attributes, Feng's Adaptive RED queue requires following
attributes:
* FengAdaptive (Boolean attribute, Default: false)
* Status (status of current queue length, Default: Above)
* FengAlpha (increment parameter for m_curMaxP, Default: 3)
* FengBeta (decrement parameter for m_curMaxP, Default: 2)
Consult the ns-3 documentation for explanation of these attributes.
Simulating ARED
@@ -133,6 +151,16 @@ To configure (ii); AdaptMaxP must be set to true, as done in
Config::SetDefault ("ns3::RedQueueDisc::AdaptMaxP", BooleanValue (true));
Simulating Feng's Adaptive RED
==============================
To switch on Feng's Adaptive RED algorithm, the attribute FengAdaptive must be
set to true, as done in ``examples/traffic-control/red-vs-fengadaptive.cc``:
.. sourcecode:: cpp
Config::SetDefault ("ns3::RedQueueDisc::FengAdaptive", BooleanValue (true));
Examples
========
@@ -142,6 +170,9 @@ ARED queue examples can be found at:
``src/traffic-control/examples/adaptive-red-tests.cc`` and
``src/traffic-control/examples/red-vs-ared.cc``
Feng's Adaptive RED example can be found at:
``examples/traffic-control/red-vs-fengadaptive.cc``
Validation
**********

View File

@@ -114,6 +114,11 @@ TypeId RedQueueDisc::GetTypeId (void)
BooleanValue (false),
MakeBooleanAccessor (&RedQueueDisc::m_isAdaptMaxP),
MakeBooleanChecker ())
.AddAttribute ("FengAdaptive",
"True to enable Feng's Adaptive RED",
BooleanValue (false),
MakeBooleanAccessor (&RedQueueDisc::m_isFengAdaptive),
MakeBooleanChecker ())
.AddAttribute ("MinTh",
"Minimum average length threshold in packets/bytes",
DoubleValue (5),
@@ -169,6 +174,16 @@ TypeId RedQueueDisc::GetTypeId (void)
DoubleValue (0.9),
MakeDoubleAccessor (&RedQueueDisc::SetAredBeta),
MakeDoubleChecker <double> (0, 1))
.AddAttribute ("FengAlpha",
"Decrement parameter for m_curMaxP in Feng's Adaptive RED",
DoubleValue (3.0),
MakeDoubleAccessor (&RedQueueDisc::SetFengAdaptiveA),
MakeDoubleChecker <double> ())
.AddAttribute ("FengBeta",
"Increment parameter for m_curMaxP in Feng's Adaptive RED",
DoubleValue (2.0),
MakeDoubleAccessor (&RedQueueDisc::SetFengAdaptiveB),
MakeDoubleChecker <double> ())
.AddAttribute ("LastSet",
"Store the last time m_curMaxP was updated",
TimeValue (Seconds (0.0)),
@@ -281,6 +296,44 @@ RedQueueDisc::GetAredBeta (void)
return m_beta;
}
void
RedQueueDisc::SetFengAdaptiveA (double a)
{
NS_LOG_FUNCTION (this << a);
m_a = a;
if (m_a != 3)
{
NS_LOG_WARN ("Alpha value does not follow the recommendations!");
}
}
double
RedQueueDisc::GetFengAdaptiveA (void)
{
NS_LOG_FUNCTION (this);
return m_a;
}
void
RedQueueDisc::SetFengAdaptiveB (double b)
{
NS_LOG_FUNCTION (this << b);
m_b = b;
if (m_b != 2)
{
NS_LOG_WARN ("Beta value does not follow the recommendations!");
}
}
double
RedQueueDisc::GetFengAdaptiveB (void)
{
NS_LOG_FUNCTION (this);
return m_b;
}
void
RedQueueDisc::SetQueueLimit (uint32_t lim)
{
@@ -465,6 +518,12 @@ RedQueueDisc::InitializeParams (void)
m_isAdaptMaxP = true;
}
if (m_isFengAdaptive)
{
// Initialize m_fengStatus
m_fengStatus = Above;
}
if (m_minTh == 0 && m_maxTh == 0)
{
m_minTh = 5.0;
@@ -568,6 +627,30 @@ RedQueueDisc::InitializeParams (void)
<< m_vC << "; m_vD " << m_vD);
}
// Updating m_curMaxP, following the pseudocode
// from: A Self-Configuring RED Gateway, INFOCOMM '99.
// They recommend m_a = 3, and m_b = 2.
void
RedQueueDisc::UpdateMaxPFeng (double newAve)
{
NS_LOG_FUNCTION (this << newAve);
if (m_minTh < newAve && newAve < m_maxTh)
{
m_fengStatus = Between;
}
else if (newAve < m_minTh && m_fengStatus != Below)
{
m_fengStatus = Below;
m_curMaxP = m_curMaxP / m_a;
}
else if (newAve > m_maxTh && m_fengStatus != Above)
{
m_fengStatus = Above;
m_curMaxP = m_curMaxP * m_b;
}
}
// Update m_curMaxP to keep the average queue length within the target range.
void
RedQueueDisc::UpdateMaxP (double newAve)
@@ -610,6 +693,10 @@ RedQueueDisc::Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW)
{
UpdateMaxP(newAve);
}
else if (m_isFengAdaptive)
{
UpdateMaxPFeng (newAve); // Update MaxP in MIMD fashion.
}
return newAve;
}
@@ -883,6 +970,11 @@ RedQueueDisc::CheckConfig (void)
return false;
}
if ((m_isARED || m_isAdaptMaxP) && m_isFengAdaptive)
{
NS_LOG_ERROR ("m_isAdaptMaxP and m_isFengAdaptive cannot be simultaneously true");
}
return true;
}

View File

@@ -98,6 +98,16 @@ public:
*/
virtual ~RedQueueDisc ();
/**
* \brief Used in Feng's Adaptive RED
*/
enum FengStatus
{
Above, //!< When m_qAvg > m_maxTh
Between, //!< When m_maxTh < m_qAvg < m_minTh
Below, //!< When m_qAvg < m_minTh
};
/**
* \brief Stats
*/
@@ -179,6 +189,34 @@ public:
*/
double GetAredBeta (void);
/**
* \brief Set the alpha value to adapt m_curMaxP in Feng's Adaptive RED.
*
* \param a The value of alpha to adapt m_curMaxP in Feng's Adaptive RED.
*/
void SetFengAdaptiveA (double a);
/**
* \brief Get the alpha value to adapt m_curMaxP in Feng's Adaptive RED.
*
* \returns The alpha value to adapt m_curMaxP in Feng's Adaptive RED.
*/
double GetFengAdaptiveA (void);
/**
* \brief Set the beta value to adapt m_curMaxP in Feng's Adaptive RED.
*
* \param b The value of beta to adapt m_curMaxP in Feng's Adaptive RED.
*/
void SetFengAdaptiveB (double b);
/**
* \brief Get the beta value to adapt m_curMaxP in Feng's Adaptive RED.
*
* \returns The beta value to adapt m_curMaxP in Feng's Adaptive RED.
*/
double GetFengAdaptiveB (void);
/**
* \brief Set the limit of the queue.
*
@@ -246,6 +284,11 @@ private:
* \param newAve new average queue length
*/
void UpdateMaxP (double newAve);
/**
* \brief Update m_curMaxP based on Feng's Adaptive RED
* \param newAve new average queue length
*/
void UpdateMaxPFeng (double newAve);
/**
* \brief Check if a packet needs to be dropped due to probability mark
* \param item queue item
@@ -302,6 +345,9 @@ private:
double m_alpha; //!< Increment parameter for m_curMaxP in ARED
double m_beta; //!< Decrement parameter for m_curMaxP in ARED
Time m_rtt; //!< Rtt to be considered while automatically setting m_bottom in ARED
bool m_isFengAdaptive; //!< True to enable Feng's Adaptive RED
double m_b; //!< Increment parameter for m_curMaxP in Feng's Adaptive RED
double m_a; //!< Decrement parameter for m_curMaxP in Feng's Adaptive RED
bool m_isNs1Compat; //!< Ns-1 compatibility
DataRate m_linkBandwidth; //!< Link bandwidth
Time m_linkDelay; //!< Link delay
@@ -323,6 +369,7 @@ private:
double m_ptc; //!< packet time constant in packets/second
double m_qAvg; //!< Average queue length
uint32_t m_count; //!< Number of packets since last random number generation
FengStatus m_fengStatus; //!< For use in Feng's Adaptive RED
/**
* 0 for default RED
* 1 experimental (see red-queue-disc.cc)

View File

@@ -237,6 +237,8 @@ RedQueueDiscTestCase::RunRedTest (StringValue mode)
uint32_t test5;
uint32_t test6;
uint32_t test7;
uint32_t test11;
uint32_t test12;
} drop;
@@ -421,6 +423,56 @@ RedQueueDiscTestCase::RunRedTest (StringValue mode)
// Packets are ECN capable, RED queue disc is ECN enabled; there should be only unforced marks, no unforced drops
NS_TEST_EXPECT_MSG_EQ (st.unforcedDrop, 0, "There should be no unforced drops");
NS_TEST_EXPECT_MSG_NE (st.unforcedMark, 0, "There should be some unforced marks");
// test 11: Original RED with default parameter settings and fixed m_curMaxP
queue = CreateObject<RedQueueDisc> ();
minTh = 30 * modeSize;
maxTh = 90 * modeSize;
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mode", mode), true,
"Verify that we can actually set the attribute Mode");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MinTh", DoubleValue (minTh)), true,
"Verify that we can actually set the attribute MinTh");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxTh", DoubleValue (maxTh)), true,
"Verify that we can actually set the attribute MaxTh");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QueueLimit", UintegerValue (qSize)), true,
"Verify that we can actually set the attribute QueueLimit");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QW", DoubleValue (0.002)), true,
"Verify that we can actually set the attribute QW");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("LInterm", DoubleValue (2)), true,
"Verify that we can actually set the attribute LInterm");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Gentle", BooleanValue (true)), true,
"Verify that we can actually set the attribute Gentle");
queue->Initialize ();
Enqueue (queue, pktSize, 300, false);
st = StaticCast<RedQueueDisc> (queue)->GetStats ();
drop.test11 = st.unforcedDrop;
NS_TEST_EXPECT_MSG_NE (drop.test11, 0, "There should some dropped packets due to probability mark");
// test 12: Feng's Adaptive RED with default parameter settings and varying m_curMaxP
queue = CreateObject<RedQueueDisc> ();
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mode", mode), true,
"Verify that we can actually set the attribute Mode");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MinTh", DoubleValue (minTh)), true,
"Verify that we can actually set the attribute MinTh");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxTh", DoubleValue (maxTh)), true,
"Verify that we can actually set the attribute MaxTh");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QueueLimit", UintegerValue (qSize)), true,
"Verify that we can actually set the attribute QueueLimit");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QW", DoubleValue (0.002)), true,
"Verify that we can actually set the attribute QW");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("LInterm", DoubleValue (2)), true,
"Verify that we can actually set the attribute LInterm");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Gentle", BooleanValue (true)), true,
"Verify that we can actually set the attribute Gentle");
NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("FengAdaptive", BooleanValue (true)), true,
"Verify that we can actually set the attribute FengAdaptive");
queue->Initialize ();
Enqueue (queue, pktSize, 300, false);
st = StaticCast<RedQueueDisc> (queue)->GetStats ();
drop.test12 = st.unforcedDrop;
NS_TEST_EXPECT_MSG_LT (drop.test12, drop.test11, "Test 12 should have less drops due to probability mark than test 11");
}
void