diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ef9547a79..f3e466d9f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -25,6 +25,7 @@ New user-visible features - (internet) Added TCP Vegas congestion control algorithm - (internet) Added TCP Scalable congestion control algorithm - (internet) Added TCP Veno congestion control algorithm +- (internet) Added TCP Bic congestion control algorithm Bugs fixed ---------- diff --git a/examples/tcp/tcp-variants-comparison.cc b/examples/tcp/tcp-variants-comparison.cc index 051ea4ef0..dde289d91 100644 --- a/examples/tcp/tcp-variants-comparison.cc +++ b/examples/tcp/tcp-variants-comparison.cc @@ -179,7 +179,7 @@ int main (int argc, char *argv[]) CommandLine cmd; cmd.AddValue ("transport_prot", "Transport protocol to use: TcpNewReno, " "TcpHybla, TcpHighSpeed, TcpVegas, TcpScalable, TcpVeno, " - "TcpWestwood, TcpWestwoodPlus ", transport_prot); + "TcpBic, TcpWestwood, TcpWestwoodPlus ", transport_prot); cmd.AddValue ("error_p", "Packet error rate", error_p); cmd.AddValue ("bandwidth", "Bottleneck bandwidth", bandwidth); cmd.AddValue ("delay", "Bottleneck delay", delay); @@ -250,6 +250,10 @@ int main (int argc, char *argv[]) { Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpVeno::GetTypeId ())); } + else if (transport_prot.compare ("TcpBic") == 0) + { + Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpBic::GetTypeId ())); + } else if (transport_prot.compare ("TcpWestwood") == 0) { // the default protocol type in ns3::TcpWestwood is WESTWOOD Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpWestwood::GetTypeId ())); @@ -368,6 +372,7 @@ int main (int argc, char *argv[]) || transport_prot.compare ("TcpHighSpeed") == 0 || transport_prot.compare ("TcpVegas") == 0 || transport_prot.compare ("TcpVeno") == 0 + || transport_prot.compare ("TcpBic") == 0 || transport_prot.compare ("TcpScalable") == 0) { Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (tcp_adu_size)); diff --git a/src/internet/doc/tcp.rst b/src/internet/doc/tcp.rst index 15a60dd7a..506a20a5c 100644 --- a/src/internet/doc/tcp.rst +++ b/src/internet/doc/tcp.rst @@ -306,6 +306,29 @@ rate as in Reno. More informations at: http://dx.doi.org/10.1109/JSAC.2002.807336 +Bic +^^^ + +In TCP Bic the congestion control problem is viewed as a search +problem. Taking as a starting point the current window value +and as a target point the last maximum window value +(i.e. the cWnd value just before the loss event) a binary search +technique can be used to update the cWnd value at the midpoint between +the two, directly or using an additive increase strategy if the distance from +the current window is too large. + +This way, assuming a no-loss period, the congestion window logarithmically +approaches the maximum value of cWnd until the difference between it and cWnd +falls below a preset threshold. After reaching such a value (or the maximum +window is unknown, i.e. the binary search does not start at all) the algorithm +switches to probing the new maximum window with a 'slow start' strategy. + +If a loss occur in either these phases, the current window (before the loss) +can be treated as the new maximum, and the reduced (with a multiplicative +decrease factor Beta) window size can be used as the new minimum. + +More informations at: http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=1354672 + Validation ++++++++++ diff --git a/src/internet/model/tcp-bic.cc b/src/internet/model/tcp-bic.cc new file mode 100644 index 000000000..2c601c281 --- /dev/null +++ b/src/internet/model/tcp-bic.cc @@ -0,0 +1,275 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2014 Natale Patriciello + * + * 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 + * + */ +#include "tcp-bic.h" +#include "ns3/log.h" +#include "ns3/tcp-socket-base.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpBic"); +NS_OBJECT_ENSURE_REGISTERED (TcpBic); + +TypeId +TcpBic::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpBic") + .SetParent () + .AddConstructor () + .SetGroupName ("Internet") + .AddAttribute ("FastConvergence", "Turn on/off fast convergence.", + BooleanValue (true), + MakeBooleanAccessor (&TcpBic::m_fastConvergence), + MakeBooleanChecker ()) + .AddAttribute ("Beta", "Beta for multiplicative decrease", + DoubleValue (0.8), + MakeDoubleAccessor (&TcpBic::m_beta), + MakeDoubleChecker (0.0)) + .AddAttribute ("MaxIncr", "Limit on increment allowed during binary search", + UintegerValue (16), + MakeUintegerAccessor (&TcpBic::m_maxIncr), + MakeUintegerChecker (1)) + .AddAttribute ("LowWnd", "Threshold window size (in segments) for engaging BIC response", + UintegerValue (14), + MakeUintegerAccessor (&TcpBic::m_lowWnd), + MakeUintegerChecker ()) + .AddAttribute ("SmoothPart", "Number of RTT needed to approach cWnd_max from " + "cWnd_max-BinarySearchCoefficient. It can be viewed as the gradient " + "of the slow start AIM phase: less this value is, " + "more steep the increment will be.", + IntegerValue (5), + MakeIntegerAccessor (&TcpBic::m_smoothPart), + MakeIntegerChecker (1)) + .AddAttribute ("BinarySearchCoefficient", "Inverse of the coefficient for the " + "binary search. Default 4, as in Linux", + UintegerValue (4), + MakeUintegerAccessor (&TcpBic::m_b), + MakeUintegerChecker (2)) + ; + return tid; +} + + +TcpBic::TcpBic () + : TcpCongestionOps (), + m_cWndCnt (0), + m_lastMaxCwnd (0), + m_lastCwnd (0), + m_epochStart (Time::Min ()) +{ + NS_LOG_FUNCTION (this); +} + +TcpBic::TcpBic (const TcpBic &sock) + : TcpCongestionOps (sock), + m_cWndCnt (sock.m_cWndCnt), + m_lastMaxCwnd (sock.m_lastMaxCwnd), + m_lastCwnd (sock.m_lastCwnd), + m_epochStart (sock.m_epochStart) +{ + NS_LOG_FUNCTION (this); +} + +void +TcpBic::IncreaseWindow (Ptr tcb, uint32_t segmentsAcked) +{ + NS_LOG_FUNCTION (this << tcb << segmentsAcked); + + if (tcb->m_cWnd < tcb->m_ssThresh) + { + tcb->m_cWnd += tcb->m_segmentSize; + segmentsAcked -= 1; + + NS_LOG_INFO ("In SlowStart, updated to cwnd " << tcb->m_cWnd << + " ssthresh " << tcb->m_ssThresh); + } + + if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0) + { + m_cWndCnt += segmentsAcked; + uint32_t cnt = Update (tcb); + + /* According to the BIC paper and RFC 6356 even once the new cwnd is + * calculated you must compare this to the number of ACKs received since + * the last cwnd update. If not enough ACKs have been received then cwnd + * cannot be updated. + */ + if (m_cWndCnt > cnt) + { + tcb->m_cWnd += tcb->m_segmentSize; + m_cWndCnt = 0; + NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd); + } + else + { + NS_LOG_INFO ("Not enough segments have been ACKed to increment cwnd." + "Until now " << m_cWndCnt); + } + } +} + +uint32_t +TcpBic::Update (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + + uint32_t segCwnd = tcb->GetCwndInSegments (); + uint32_t cnt; + + m_lastCwnd = segCwnd; + + if (m_epochStart == Time::Min ()) + { + m_epochStart = Simulator::Now (); /* record the beginning of an epoch */ + } + + if (segCwnd < m_lowWnd) + { + NS_LOG_INFO ("Under lowWnd, compatibility mode. Behaving as NewReno"); + cnt = segCwnd; + return cnt; + } + + if (segCwnd < m_lastMaxCwnd) + { + double dist = (m_lastMaxCwnd - segCwnd) / m_b; + + NS_LOG_INFO ("cWnd = " << segCwnd << " under lastMax, " << + m_lastMaxCwnd << " and dist=" << dist); + if (dist > m_maxIncr) + { + /* Linear increase */ + cnt = segCwnd / m_maxIncr; + NS_LOG_INFO ("Linear increase (maxIncr=" << m_maxIncr << "), cnt=" << cnt); + } + else if (dist <= 1) + { + /* smoothed binary search increase: when our window is really + * close to the last maximum, we parameterize in m_smoothPart the number + * of RTT needed to reach that window. + */ + cnt = (segCwnd * m_smoothPart) / m_b; + + NS_LOG_INFO ("Binary search increase (smoothPart=" << m_smoothPart << + "), cnt=" << cnt); + } + else + { + /* binary search increase */ + cnt = segCwnd / dist; + + NS_LOG_INFO ("Binary search increase, cnt=" << cnt); + } + } + else + { + NS_LOG_INFO ("cWnd = " << segCwnd << " above last max, " << + m_lastMaxCwnd); + if (segCwnd < m_lastMaxCwnd + m_b) + { + /* slow start AMD linear increase */ + cnt = (segCwnd * m_smoothPart) / m_b; + NS_LOG_INFO ("Slow start AMD, cnt=" << cnt); + } + else if (segCwnd < m_lastMaxCwnd + m_maxIncr * (m_b - 1)) + { + /* slow start */ + cnt = (segCwnd * (m_b - 1)) / (segCwnd - m_lastMaxCwnd); + + NS_LOG_INFO ("Slow start, cnt=" << cnt); + } + else + { + /* linear increase */ + cnt = segCwnd / m_maxIncr; + + NS_LOG_INFO ("Linear, cnt=" << cnt); + } + } + + /* if in slow start or link utilization is very low. Code taken from Linux + * kernel, not sure of the source they take it. Usually, it is not reached, + * since if m_lastMaxCwnd is 0, we are (hopefully) in slow start. + */ + if (m_lastMaxCwnd == 0) + { + if (cnt > 20) /* increase cwnd 5% per RTT */ + { + cnt = 20; + } + } + + if (cnt == 0) + { + cnt = 1; + } + + return cnt; +} + +std::string +TcpBic::GetName () const +{ + return "TcpBic"; +} + +uint32_t +TcpBic::GetSsThresh (Ptr tcb, uint32_t bytesInFlight) +{ + NS_LOG_FUNCTION (this); + + uint32_t segCwnd = tcb->GetCwndInSegments (); + uint32_t ssThresh = 0; + + m_epochStart = Time::Min (); + + /* Wmax and fast convergence */ + if (segCwnd < m_lastMaxCwnd && m_fastConvergence) + { + NS_LOG_INFO ("Fast Convergence. Last max cwnd: " << m_lastMaxCwnd << + " updated to " << (uint32_t) m_beta * segCwnd); + m_lastMaxCwnd = m_beta * segCwnd; + } + else + { + NS_LOG_INFO ("Last max cwnd: " << m_lastMaxCwnd << " updated to " << + segCwnd); + m_lastMaxCwnd = segCwnd; + } + + if (segCwnd < m_lowWnd) + { + ssThresh = std::max (2 * tcb->m_segmentSize, bytesInFlight / 2); + NS_LOG_INFO ("Less than lowWindow, ssTh= " << ssThresh); + } + else + { + ssThresh = std::max (segCwnd * m_beta, 2.0) * tcb->m_segmentSize; + NS_LOG_INFO ("More than lowWindow, ssTh= " << ssThresh); + } + + return ssThresh; +} + +Ptr +TcpBic::Fork (void) +{ + return CopyObject (this); +} + +} // namespace ns3 diff --git a/src/internet/model/tcp-bic.h b/src/internet/model/tcp-bic.h new file mode 100644 index 000000000..313d382ac --- /dev/null +++ b/src/internet/model/tcp-bic.h @@ -0,0 +1,123 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2014 Natale Patriciello + * + * 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 + * + */ + +#ifndef TCPBIC_H +#define TCPBIC_H + +#include "ns3/tcp-congestion-ops.h" +#include "ns3/traced-value.h" + +namespace ns3 { + +/** \brief BIC congestion control algorithm + * + * In TCP Bic the congestion control problem is viewed as a search + * problem. Taking as a starting point the current window value + * and as a target point the last maximum window value + * (i.e. the cWnd value just before the loss event) a binary search + * technique can be used to update the cWnd value at the midpoint between + * the two, directly or using an additive increase strategy if the distance from + * the current window is too large. + * + * This way, assuming a no-loss period, the congestion window logarithmically + * approaches the maximum value of cWnd until the difference between it and cWnd + * falls below a preset threshold. After reaching such a value (or the maximum + * window is unknown, i.e. the binary search does not start at all) the algorithm + * switches to probing the new maximum window with a 'slow start' strategy. + * + * If a loss occur in either these phases, the current window (before the loss) + * can be treated as the new maximum, and the reduced (with a multiplicative + * decrease factor Beta) window size can be used as the new minimum. + * + * To maintain the performance of TCP Bic as close as possible with the Linux + * implementation, and at the same time maintain the friendliness with other TCP + * flavors, the cWnd is increased only after a certain number of ACKs + * are received, following RFC 6356. After the slow start phase, and after each + * new ACK, a value is calculated by the method Update. This number + * (m_cnt in the code) represents the ACK packets that should be received + * before increasing the cWnd by one segment. After a trivial check on the + * arrived ACKs (represented by m_cWndCnt in the code), the + * cWnd can be increased and m_cWndCnt can be set to zero, or + * otherwise m_cWndCnt can be increased by one and the + * cWnd can be left untouched. + * + * The binary search on the cWnd size space is done by varying the returned + * cnt, depending on the internal state of the class (e.g. the last maximum + * and the current cWnd size). + * + * The reference paper for BIC can be found in: + * http://an.kaist.ac.kr/courses/2006/cs540/reading/bic-tcp.pdf + * + * This model has a number of configurable parameters that are exposed as + * attributes of the TcpBic TypeId. This model also exports trace sources, + * for tracking the congestion window, slow start threshold, and the internal + * state of the protocol. + * + * More information on this implementation: http://dl.acm.org/citation.cfm?id=2756518 + */ +class TcpBic : public TcpCongestionOps +{ +public: + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * \brief Constructor + */ + TcpBic (); + TcpBic (const TcpBic &sock); + + virtual std::string GetName () const; + virtual void IncreaseWindow (Ptr tcb, + uint32_t segmentsAcked); + virtual uint32_t GetSsThresh (Ptr tcb, + uint32_t bytesInFlight); + + virtual Ptr Fork (); + +protected: + /** + * \brief Bic window update after a new ack received + */ + virtual uint32_t Update (Ptr tcb); + +private: + friend class TcpBicIncrementTest; + friend class TcpBicDecrementTest; + + // User parameters + bool m_fastConvergence; //!< Enable or disable fast convergence algorithm + double m_beta; //!< Beta for cubic multiplicative increase + uint32_t m_maxIncr; //!< Maximum window increment + uint32_t m_lowWnd; //!< Lower bound on congestion window + int m_smoothPart; //!< Number of RTT needed to reach Wmax from Wmax-B + + // Bic parameters + uint32_t m_cWndCnt; //!< cWnd integer-to-float counter + uint32_t m_lastMaxCwnd; //!< Last maximum cWnd + uint32_t m_lastCwnd; //!< Last cWnd + Time m_epochStart; //!< Beginning of an epoch + uint8_t m_b; //!< Binary search coefficient +}; + +} // namespace ns3 +#endif // TCPBIC_H diff --git a/src/internet/wscript b/src/internet/wscript index 15cda4de1..6e4d4ba53 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -152,6 +152,7 @@ def build(bld): 'model/tcp-westwood.cc', 'model/tcp-scalable.cc', 'model/tcp-veno.cc', + 'model/tcp-bic.cc', 'model/tcp-rx-buffer.cc', 'model/tcp-tx-buffer.cc', 'model/tcp-option.cc', @@ -361,6 +362,7 @@ def build(bld): 'model/tcp-westwood.h', 'model/tcp-scalable.h', 'model/tcp-veno.h', + 'model/tcp-bic.h', 'model/tcp-socket-base.h', 'model/tcp-tx-buffer.h', 'model/tcp-rx-buffer.h',