diff --git a/.jenkins.d/01-deps.sh b/.jenkins.d/01-deps.sh index 43c81f8b2..db2d80879 100755 --- a/.jenkins.d/01-deps.sh +++ b/.jenkins.d/01-deps.sh @@ -20,6 +20,6 @@ if has Ubuntu $NODE_LABELS; then sudo apt-get -qq install build-essential pkg-config libboost-all-dev \ libcrypto++-dev libsqlite3-dev libssl-dev sudo apt-get install -qq -y python-setuptools python-dev python-pygraphviz python-kiwi - sudo apt-get install -qq -y python-pygoocanvas python-gnome2 - sudo apt-get install -qq -y python-rsvg ipython + # sudo apt-get install -qq -y python-pygoocanvas python-gnome2 + # sudo apt-get install -qq -y python-rsvg ipython fi diff --git a/apps/ndn-consumer-pcon.cpp b/apps/ndn-consumer-pcon.cpp new file mode 100644 index 000000000..22427407c --- /dev/null +++ b/apps/ndn-consumer-pcon.cpp @@ -0,0 +1,346 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011-2018 Regents of the University of California. + * + * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and + * contributors. + * + * ndnSIM is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * ndnSIM 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 + * ndnSIM, e.g., in COPYING.md file. If not, see . + **/ + +#include "ndn-consumer-pcon.hpp" + +NS_LOG_COMPONENT_DEFINE("ndn.ConsumerPcon"); + +namespace ns3 { +namespace ndn { + +NS_OBJECT_ENSURE_REGISTERED(ConsumerPcon); + +constexpr double ConsumerPcon::CUBIC_C; +constexpr uint32_t ConsumerPcon::BIC_MAX_INCREMENT; +constexpr uint32_t ConsumerPcon::BIC_LOW_WINDOW; + +TypeId +ConsumerPcon::GetTypeId() +{ + static TypeId tid = + TypeId("ns3::ndn::ConsumerPcon") + .SetGroupName("Ndn") + .SetParent() + .AddConstructor() + + .AddAttribute("CcAlgorithm", + "Specify which window adaptation algorithm to use (AIMD, BIC, or CUBIC)", + EnumValue(CcAlgorithm::AIMD), + MakeEnumAccessor(&ConsumerPcon::m_ccAlgorithm), + MakeEnumChecker(CcAlgorithm::AIMD, "AIMD", CcAlgorithm::BIC, "BIC", + CcAlgorithm::CUBIC, "CUBIC")) + + .AddAttribute("Beta", + "TCP Multiplicative Decrease factor", + DoubleValue(0.5), + MakeDoubleAccessor(&ConsumerPcon::m_beta), + MakeDoubleChecker()) + + .AddAttribute("CubicBeta", + "TCP CUBIC Multiplicative Decrease factor", + DoubleValue(0.8), + MakeDoubleAccessor(&ConsumerPcon::m_cubicBeta), + MakeDoubleChecker()) + + .AddAttribute("AddRttSuppress", + "Minimum number of RTTs (1 + this factor) between window decreases", + DoubleValue(0.5), // This default value was chosen after manual testing + MakeDoubleAccessor(&ConsumerPcon::m_addRttSuppress), + MakeDoubleChecker()) + + .AddAttribute("ReactToCongestionMarks", + "If true, process received congestion marks", + BooleanValue(true), + MakeBooleanAccessor(&ConsumerPcon::m_reactToCongestionMarks), + MakeBooleanChecker()) + + .AddAttribute("UseCwa", + "If true, use Conservative Window Adaptation", + BooleanValue(true), + MakeBooleanAccessor(&ConsumerPcon::m_useCwa), + MakeBooleanChecker()) + + .AddAttribute("UseCubicFastConvergence", + "If true, use TCP CUBIC Fast Convergence", + BooleanValue(false), + MakeBooleanAccessor(&ConsumerPcon::m_useCubicFastConv), + MakeBooleanChecker()); + + return tid; +} + +ConsumerPcon::ConsumerPcon() + : m_ssthresh(std::numeric_limits::max()) + , m_highData(0) + , m_recPoint(0.0) + , m_cubicWmax(0) + , m_cubicLastWmax(0) + , m_cubicLastDecrease(time::steady_clock::now()) + , m_bicMinWin(0) + , m_bicMaxWin(std::numeric_limits::max()) + , m_bicTargetWin(0) + , m_bicSsCwnd(0) + , m_bicSsTarget(0) + , m_isBicSs(false) +{ +} + +void +ConsumerPcon::OnData(shared_ptr data) +{ + Consumer::OnData(data); + + uint64_t sequenceNum = data->getName().get(-1).toSequenceNumber(); + + // Set highest received Data to sequence number + if (m_highData < sequenceNum) { + m_highData = sequenceNum; + } + + if (data->getCongestionMark() > 0) { + if (m_reactToCongestionMarks) { + NS_LOG_DEBUG("Received congestion mark: " << data->getCongestionMark()); + WindowDecrease(); + } + else { + NS_LOG_DEBUG("Ignored received congestion mark: " << data->getCongestionMark()); + } + } + else { + WindowIncrease(); + } + + if (m_inFlight > static_cast(0)) { + m_inFlight--; + } + + NS_LOG_DEBUG("Window: " << m_window << ", InFlight: " << m_inFlight); + + ScheduleNextPacket(); +} + +void +ConsumerPcon::OnTimeout(uint32_t sequenceNum) +{ + WindowDecrease(); + + if (m_inFlight > static_cast(0)) { + m_inFlight--; + } + + NS_LOG_DEBUG("Window: " << m_window << ", InFlight: " << m_inFlight); + + Consumer::OnTimeout(sequenceNum); +} + +void +ConsumerPcon::WindowIncrease() +{ + if (m_ccAlgorithm == CcAlgorithm::AIMD) { + if (m_window < m_ssthresh) { + m_window += 1.0; + } + else { + m_window += (1.0 / m_window); + } + } + else if (m_ccAlgorithm == CcAlgorithm::CUBIC) { + CubicIncrease(); + } + else if (m_ccAlgorithm == CcAlgorithm::BIC) { + BicIncrease(); + } + else { + BOOST_ASSERT_MSG(false, "Unknown CC Algorithm"); + } + NS_LOG_DEBUG("Window size increased to " << m_window); +} + +void +ConsumerPcon::WindowDecrease() +{ + if (!m_useCwa || m_highData > m_recPoint) { + const double diff = m_seq - m_highData; + BOOST_ASSERT(diff > 0); + + m_recPoint = m_seq + (m_addRttSuppress * diff); + + if (m_ccAlgorithm == CcAlgorithm::AIMD) { + // Normal TCP Decrease: + m_ssthresh = m_window * m_beta; + m_window = m_ssthresh; + } + else if (m_ccAlgorithm == CcAlgorithm::CUBIC) { + CubicDecrease(); + } + else if (m_ccAlgorithm == CcAlgorithm::BIC) { + BicDecrease(); + } + else { + BOOST_ASSERT_MSG(false, "Unknown CC Algorithm"); + } + + // Window size cannot be reduced below initial size + if (m_window < m_initialWindow) { + m_window = m_initialWindow; + } + + NS_LOG_DEBUG("Window size decreased to " << m_window); + } + else { + NS_LOG_DEBUG("Window decrease suppressed, HighData: " << m_highData << ", RecPoint: " << m_recPoint); + } +} + + +void +ConsumerPcon::BicIncrease() +{ + if (m_window < BIC_LOW_WINDOW) { + // Normal TCP AIMD behavior + if (m_window < m_ssthresh) { + m_window = m_window + 1; + } + else { + m_window = m_window + 1.0 / m_window; + } + } + else if (!m_isBicSs) { + // Binary increase + if (m_bicTargetWin - m_window < BIC_MAX_INCREMENT) { // Binary search + m_window += (m_bicTargetWin - m_window) / m_window; + } + else { + m_window += BIC_MAX_INCREMENT / m_window; // Additive increase + } + // FIX for equal double values. + if (m_window + 0.00001 < m_bicMaxWin) { + m_bicMinWin = m_window; + m_bicTargetWin = (m_bicMaxWin + m_bicMinWin) / 2; + } + else { + m_isBicSs = true; + m_bicSsCwnd = 1; + m_bicSsTarget = m_window + 1.0; + m_bicMaxWin = std::numeric_limits::max(); + } + } + else { + // BIC slow start + m_window += m_bicSsCwnd / m_window; + if (m_window >= m_bicSsTarget) { + m_bicSsCwnd = 2 * m_bicSsCwnd; + m_bicSsTarget = m_window + m_bicSsCwnd; + } + if (m_bicSsCwnd >= BIC_MAX_INCREMENT) { + m_isBicSs = false; + } + } +} + +void +ConsumerPcon::BicDecrease() +{ + // BIC Decrease + if (m_window >= BIC_LOW_WINDOW) { + auto prev_max = m_bicMaxWin; + m_bicMaxWin = m_window; + m_window = m_window * m_cubicBeta; + m_bicMinWin = m_window; + if (prev_max > m_bicMaxWin) { + // Fast Convergence + m_bicMaxWin = (m_bicMaxWin + m_bicMinWin) / 2; + } + m_bicTargetWin = (m_bicMaxWin + m_bicMinWin) / 2; + } + else { + // Normal TCP Decrease: + m_ssthresh = m_window * m_cubicBeta; + m_window = m_ssthresh; + } +} + + +void +ConsumerPcon::CubicIncrease() +{ + // 1. Time since last congestion event in Seconds + const double t = time::duration_cast( + time::steady_clock::now() - m_cubicLastDecrease).count() / 1e6; + + // 2. Time it takes to increase the window to cubic_wmax + // K = cubic_root(W_max*(1-beta_cubic)/C) (Eq. 2) + const double k = std::cbrt(m_cubicWmax * (1 - m_cubicBeta) / CUBIC_C); + + // 3. Target: W_cubic(t) = C*(t-K)^3 + W_max (Eq. 1) + const double w_cubic = CUBIC_C * std::pow(t - k, 3) + m_cubicWmax; + + // 4. Estimate of Reno Increase (Currently Disabled) + // const double rtt = m_rtt->GetCurrentEstimate().GetSeconds(); + // const double w_est = m_cubic_wmax*m_beta + (3*(1-m_beta)/(1+m_beta)) * (t/rtt); + constexpr double w_est = 0.0; + + // Actual adaptation + if (m_window < m_ssthresh) { + m_window += 1.0; + } + else { + BOOST_ASSERT(m_cubicWmax > 0); + + double cubic_increment = std::max(w_cubic, w_est) - m_window; + // Cubic increment must be positive: + // Note: This change is not part of the RFC, but I added it to improve performance. + if (cubic_increment < 0) { + cubic_increment = 0.0; + } + m_window += cubic_increment / m_window; + } +} + + +void +ConsumerPcon::CubicDecrease() +{ + // This implementation is ported from https://datatracker.ietf.org/doc/rfc8312/ + + const double FAST_CONV_DIFF = 1.0; // In percent + + // A flow remembers the last value of W_max, + // before it updates W_max for the current congestion event. + + // Current w_max < last_wmax + if (m_useCubicFastConv && m_window < m_cubicLastWmax * (1 - FAST_CONV_DIFF / 100)) { + m_cubicLastWmax = m_window; + m_cubicWmax = m_window * (1.0 + m_cubicBeta) / 2.0; + } + else { + // Save old cwnd as w_max: + m_cubicLastWmax = m_window; + m_cubicWmax = m_window; + } + + m_ssthresh = m_window * m_cubicBeta; + m_ssthresh = std::max(m_ssthresh, m_initialWindow); + m_window = m_ssthresh; + + m_cubicLastDecrease = time::steady_clock::now(); +} + +} // namespace ndn +} // namespace ns3 diff --git a/apps/ndn-consumer-pcon.hpp b/apps/ndn-consumer-pcon.hpp new file mode 100644 index 000000000..080e675c6 --- /dev/null +++ b/apps/ndn-consumer-pcon.hpp @@ -0,0 +1,117 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011-2018 Regents of the University of California. + * + * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and + * contributors. + * + * ndnSIM is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * ndnSIM 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 + * ndnSIM, e.g., in COPYING.md file. If not, see . + **/ + +#ifndef NDN_CONSUMER_PCON_H +#define NDN_CONSUMER_PCON_H + +#include "ns3/ndnSIM/model/ndn-common.hpp" + +#include "ndn-consumer-window.hpp" + +namespace ns3 { +namespace ndn { + +enum CcAlgorithm { + AIMD, + BIC, + CUBIC +}; + +/** + * @ingroup ndn-apps + * \brief NDN consumer application with more advanced congestion control options + * + * This app uses the algorithms from "A Practical Congestion Control Scheme for Named + * Data Networking" (https://dl.acm.org/citation.cfm?id=2984369). + * + * It implements slow start, conservative window adaptation (RFC 6675), + * and 3 different TCP algorithms: AIMD, BIC, and CUBIC (RFC 8312). + */ +class ConsumerPcon : public ConsumerWindow { +public: + static TypeId + GetTypeId(); + + ConsumerPcon(); + + virtual void + OnData(shared_ptr data) override; + + virtual void + OnTimeout(uint32_t sequenceNum) override; + +private: + void + WindowIncrease(); + + void + WindowDecrease(); + + void + CubicIncrease(); + + void + CubicDecrease(); + + void + BicIncrease(); + + void + BicDecrease(); + +private: + CcAlgorithm m_ccAlgorithm; + double m_beta; + double m_addRttSuppress; + bool m_reactToCongestionMarks; + bool m_useCwa; + + double m_ssthresh; + uint32_t m_highData; + double m_recPoint; + + // TCP CUBIC Parameters // + static constexpr double CUBIC_C = 0.4; + bool m_useCubicFastConv; + double m_cubicBeta; + + double m_cubicWmax; + double m_cubicLastWmax; + time::steady_clock::TimePoint m_cubicLastDecrease; + + // TCP BIC Parameters // + //! Regular TCP behavior (including slow start) until this window size + static constexpr uint32_t BIC_LOW_WINDOW = 14; + + //! Sets the maximum (linear) increase of TCP BIC. Should be between 8 and 64. + static constexpr uint32_t BIC_MAX_INCREMENT = 16; + + // BIC variables: + double m_bicMinWin; //!< last minimum cwnd + double m_bicMaxWin; //!< last maximum cwnd + double m_bicTargetWin; + double m_bicSsCwnd; + double m_bicSsTarget; + bool m_isBicSs; //!< whether we are currently in the BIC slow start phase +}; + +} // namespace ndn +} // namespace ns3 + +#endif // NDN_CONSUMER_PCON_H diff --git a/apps/ndn-consumer-window.hpp b/apps/ndn-consumer-window.hpp index 4e76c5096..c298e1711 100644 --- a/apps/ndn-consumer-window.hpp +++ b/apps/ndn-consumer-window.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (c) 2011-2015 Regents of the University of California. +/* + * Copyright (c) 2011-2018 Regents of the University of California. * * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and * contributors. @@ -33,7 +33,7 @@ namespace ndn { * \brief Ndn application for sending out Interest packets (window-based) * * !!! ATTENTION !!! This is highly experimental and relies on experimental features of the - *simulator. + * simulator. * Behavior may be unpredictable if used incorrectly. */ class ConsumerWindow : public Consumer { @@ -57,7 +57,7 @@ class ConsumerWindow : public Consumer { WillSendOutInterest(uint32_t sequenceNumber); public: - typedef void (*WindowTraceCallback)(uint32_t); + typedef std::function WindowTraceCallback; protected: /** @@ -92,18 +92,18 @@ class ConsumerWindow : public Consumer { void SetSeqMax(uint32_t seqMax); -private: +protected: uint32_t m_payloadSize; // expected payload size double m_maxSize; // max size to request uint32_t m_initialWindow; bool m_setInitialWindowOnTimeout; - TracedValue m_window; + TracedValue m_window; TracedValue m_inFlight; }; } // namespace ndn } // namespace ns3 -#endif +#endif // NDN_CONSUMER_WINDOW_H diff --git a/docs/source/applications.rst b/docs/source/applications.rst index 8bb508da1..51911468a 100644 --- a/docs/source/applications.rst +++ b/docs/source/applications.rst @@ -130,7 +130,7 @@ This applications has the following attributes: ConsumerWindow ^^^^^^^^^^^^^^^^^^ -:ndnsim:`ConsumerWindow` is an application generating a variable rate Interest traffic. It implements a simple sliding-window-based Interest generation mechanism. +:ndnsim:`ConsumerWindow` is an application generating a variable-rate Interest traffic. It implements a simple sliding-window-based Interest generation mechanism. .. code-block:: c++ @@ -163,6 +163,67 @@ This applications has the following attributes: If ``Size`` is set to -1, Interests will be requested till the end of the simulation. +ConsumerPcon +^^^^^^^^^^^^^^^^ + +:ndnsim:`ConsumerPcon` is an application generating variable-rate Interest traffic. It implements a window-based rate control that adjusts window size using the PCON congestion control mechanism developed by K. Schneider et al. (https://named-data.net/publications/practical_congestion_control_scheme/). It is derived from :ndnsim:`ConsumerWindow`. + +.. code-block:: c++ + + // Create application using the app helper + AppHelper consumerHelper("ns3::ndn::ConsumerPcon"); + +The application has the same attributes as :ndnsim:`ConsumerWindow`, in addition to the following: + +* ``CcAlgorithm`` + + .. note:: + default: ``AIMD`` + + Which window adaptation algorithm to use (AIMD, BIC, or CUBIC) + +* ``Beta`` + + .. note:: + default: ``0.5`` + + TCP Multiplicative Decrease factor + +* ``CubicBeta`` + + .. note:: + default: ``0.8`` + + TCP CUBIC Multiplicative Decrease factor + +* ``AddRttSupress`` + + .. note:: + default: ``0.5`` + + Minimum number of RTTs (1 + this factor) between window decreases + +* ``ReactToCongestionMarks`` + + .. note:: + default: ``true`` + + If true, process received congestion marks; otherwise, ignore them + +* ``UseCwa`` + + .. note:: + default: ``true`` + + If true, use Conservative Window Adaptation + +* ``UseCubicFastConvergence`` + + .. note:: + default: ``true`` + + If true, use TCP CUBIC Fast Convergence + Producer ^^^^^^^^^^^^ diff --git a/examples/ndn-grid.py b/examples/ndn-grid.py index c13c405c7..11d5f045c 100644 --- a/examples/ndn-grid.py +++ b/examples/ndn-grid.py @@ -51,7 +51,7 @@ Config.SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("10Mbps")) Config.SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms")) -Config::SetDefault("ns3::QueueBase::MaxPackets", UintegerValue(20)) +Config.SetDefault("ns3::QueueBase::MaxPackets", UintegerValue(20)) import sys; cmd = CommandLine(); cmd.Parse(sys.argv); diff --git a/examples/ndn-simple.py b/examples/ndn-simple.py index 108406a06..62ae4ca0f 100644 --- a/examples/ndn-simple.py +++ b/examples/ndn-simple.py @@ -47,7 +47,7 @@ # Set default parameters for PointToPoint links and channels Config.SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("10Mbps")) Config.SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms")) -Config::SetDefault("ns3::QueueBase::MaxPackets", UintegerValue(20)) +Config.SetDefault("ns3::QueueBase::MaxPackets", UintegerValue(20)) # Read optional command-line parameters (e.g., enable visualizer with ./waf --pyrun=<> --visualize import sys; cmd = CommandLine(); cmd.Parse(sys.argv); diff --git a/model/ndn-net-device-transport.cpp b/model/ndn-net-device-transport.cpp index 32abaa6e0..17880d279 100644 --- a/model/ndn-net-device-transport.cpp +++ b/model/ndn-net-device-transport.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (c) 2011-2016 Regents of the University of California. +/* + * Copyright (c) 2011-2018 Regents of the University of California. * * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and * contributors. @@ -27,6 +27,8 @@ #include #include +#include "ns3/queue.h" + NS_LOG_COMPONENT_DEFINE("ndn.NetDeviceTransport"); namespace ns3 { @@ -49,6 +51,14 @@ NetDeviceTransport::NetDeviceTransport(Ptr node, this->setLinkType(linkType); this->setMtu(m_netDevice->GetMtu()); // Use the MTU of the netDevice + // Get send queue capacity for congestion marking + PointerValue txQueueAttribute; + if (m_netDevice->GetAttributeFailSafe("TxQueue", txQueueAttribute)) { + Ptr txQueue = txQueueAttribute.Get(); + // must be put into bytes mode queue + this->setSendQueueCapacity(txQueue->GetMaxBytes()); + } + NS_LOG_FUNCTION(this << "Creating an ndnSIM transport instance for netDevice with URI" << this->getLocalUri()); @@ -64,6 +74,19 @@ NetDeviceTransport::~NetDeviceTransport() NS_LOG_FUNCTION_NOARGS(); } +ssize_t +NetDeviceTransport::getSendQueueLength() +{ + PointerValue txQueueAttribute; + if (m_netDevice->GetAttributeFailSafe("TxQueue", txQueueAttribute)) { + Ptr txQueue = txQueueAttribute.Get(); + return txQueue->GetNBytes(); + } + else { + return nfd::face::QUEUE_UNSUPPORTED; + } +} + void NetDeviceTransport::doClose() { diff --git a/model/ndn-net-device-transport.hpp b/model/ndn-net-device-transport.hpp index 09a08ed55..e68382d74 100644 --- a/model/ndn-net-device-transport.hpp +++ b/model/ndn-net-device-transport.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (c) 2011-2016 Regents of the University of California. +/* + * Copyright (c) 2011-2018 Regents of the University of California. * * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and * contributors. @@ -54,6 +54,9 @@ class NetDeviceTransport : public nfd::face::Transport Ptr GetNetDevice() const; + virtual ssize_t + getSendQueueLength() final; + private: virtual void doClose() override;