A Discrete-Event Network Simulator
API
tcp-cubic.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Natale Patriciello <natale.patriciello@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  */
18 
19 #define NS_LOG_APPEND_CONTEXT \
20  { \
21  std::clog << Simulator::Now().GetSeconds() << " "; \
22  }
23 
24 #include "tcp-cubic.h"
25 
26 #include "ns3/log.h"
27 
28 NS_LOG_COMPONENT_DEFINE("TcpCubic");
29 
30 namespace ns3
31 {
32 
34 
35 TypeId
37 {
38  static TypeId tid =
39  TypeId("ns3::TcpCubic")
41  .AddConstructor<TcpCubic>()
42  .SetGroupName("Internet")
43  .AddAttribute("FastConvergence",
44  "Enable (true) or disable (false) fast convergence",
45  BooleanValue(true),
48  .AddAttribute("Beta",
49  "Beta for multiplicative decrease",
50  DoubleValue(0.7),
52  MakeDoubleChecker<double>(0.0))
53  .AddAttribute("HyStart",
54  "Enable (true) or disable (false) hybrid slow start algorithm",
55  BooleanValue(true),
58  .AddAttribute("HyStartLowWindow",
59  "Lower bound cWnd for hybrid slow start (segments)",
60  UintegerValue(16),
62  MakeUintegerChecker<uint32_t>())
63  .AddAttribute("HyStartDetect",
64  "Hybrid Slow Start detection mechanisms:"
65  "packet train, delay, both",
66  EnumValue(HybridSSDetectionMode::BOTH),
68  MakeEnumChecker(HybridSSDetectionMode::PACKET_TRAIN,
69  "PACKET_TRAIN",
71  "DELAY",
72  HybridSSDetectionMode::BOTH,
73  "BOTH"))
74  .AddAttribute("HyStartMinSamples",
75  "Number of delay samples for detecting the increase of delay",
76  UintegerValue(8),
78  MakeUintegerChecker<uint8_t>())
79  .AddAttribute("HyStartAckDelta",
80  "Spacing between ack's indicating train",
84  .AddAttribute("HyStartDelayMin",
85  "Minimum time for hystart algorithm",
89  .AddAttribute("HyStartDelayMax",
90  "Maximum time for hystart algorithm",
91  TimeValue(MilliSeconds(1000)),
94  .AddAttribute("CubicDelta",
95  "Delta Time to wait after fast recovery before adjusting param",
99  .AddAttribute("CntClamp",
100  "Counter value when no losses are detected (counter is used"
101  " when incrementing cWnd in congestion avoidance, to avoid"
102  " floating point arithmetic). It is the modulo of the (avoided)"
103  " division",
104  UintegerValue(20),
106  MakeUintegerChecker<uint8_t>())
107  .AddAttribute("C",
108  "Cubic Scaling factor",
109  DoubleValue(0.4),
111  MakeDoubleChecker<double>(0.0));
112  return tid;
113 }
114 
116  : TcpCongestionOps(),
117  m_cWndCnt(0),
118  m_lastMaxCwnd(0),
119  m_bicOriginPoint(0),
120  m_bicK(0.0),
121  m_delayMin(Time::Min()),
122  m_epochStart(Time::Min()),
123  m_found(false),
124  m_roundStart(Time::Min()),
125  m_endSeq(0),
126  m_lastAck(Time::Min()),
127  m_cubicDelta(Time::Min()),
128  m_currRtt(Time::Min()),
129  m_sampleCnt(0)
130 {
131  NS_LOG_FUNCTION(this);
132 }
133 
135  : TcpCongestionOps(sock),
136  m_fastConvergence(sock.m_fastConvergence),
137  m_beta(sock.m_beta),
138  m_hystart(sock.m_hystart),
139  m_hystartDetect(sock.m_hystartDetect),
140  m_hystartLowWindow(sock.m_hystartLowWindow),
141  m_hystartAckDelta(sock.m_hystartAckDelta),
142  m_hystartDelayMin(sock.m_hystartDelayMin),
143  m_hystartDelayMax(sock.m_hystartDelayMax),
144  m_hystartMinSamples(sock.m_hystartMinSamples),
145  m_initialCwnd(sock.m_initialCwnd),
146  m_cntClamp(sock.m_cntClamp),
147  m_c(sock.m_c),
148  m_cWndCnt(sock.m_cWndCnt),
149  m_lastMaxCwnd(sock.m_lastMaxCwnd),
150  m_bicOriginPoint(sock.m_bicOriginPoint),
151  m_bicK(sock.m_bicK),
152  m_delayMin(sock.m_delayMin),
153  m_epochStart(sock.m_epochStart),
154  m_found(sock.m_found),
155  m_roundStart(sock.m_roundStart),
156  m_endSeq(sock.m_endSeq),
157  m_lastAck(sock.m_lastAck),
158  m_cubicDelta(sock.m_cubicDelta),
159  m_currRtt(sock.m_currRtt),
160  m_sampleCnt(sock.m_sampleCnt)
161 {
162  NS_LOG_FUNCTION(this);
163 }
164 
165 std::string
167 {
168  return "TcpCubic";
169 }
170 
171 void
173 {
174  NS_LOG_FUNCTION(this);
175 
177  m_endSeq = tcb->m_highTxMark;
178  m_currRtt = Time::Min();
179  m_sampleCnt = 0;
180 }
181 
182 void
183 TcpCubic::IncreaseWindow(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
184 {
185  NS_LOG_FUNCTION(this << tcb << segmentsAcked);
186 
187  if (tcb->m_cWnd < tcb->m_ssThresh)
188  {
189  if (m_hystart && tcb->m_lastAckedSeq > m_endSeq)
190  {
191  HystartReset(tcb);
192  }
193 
194  // In Linux, the QUICKACK socket option enables the receiver to send
195  // immediate acks initially (during slow start) and then transition
196  // to delayed acks. ns-3 does not implement QUICKACK, and if ack
197  // counting instead of byte counting is used during slow start window
198  // growth, when TcpSocket::DelAckCount==2, then the slow start will
199  // not reach as large of an initial window as in Linux. Therefore,
200  // we can approximate the effect of QUICKACK by making this slow
201  // start phase perform Appropriate Byte Counting (RFC 3465)
202  tcb->m_cWnd += segmentsAcked * tcb->m_segmentSize;
203  segmentsAcked = 0;
204 
205  NS_LOG_INFO("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh "
206  << tcb->m_ssThresh);
207  }
208 
209  if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
210  {
211  m_cWndCnt += segmentsAcked;
212  uint32_t cnt = Update(tcb);
213 
214  /* According to RFC 6356 even once the new cwnd is
215  * calculated you must compare this to the number of ACKs received since
216  * the last cwnd update. If not enough ACKs have been received then cwnd
217  * cannot be updated.
218  */
219  if (m_cWndCnt >= cnt)
220  {
221  tcb->m_cWnd += tcb->m_segmentSize;
222  m_cWndCnt -= cnt;
223  NS_LOG_INFO("In CongAvoid, updated to cwnd " << tcb->m_cWnd);
224  }
225  else
226  {
227  NS_LOG_INFO("Not enough segments have been ACKed to increment cwnd."
228  "Until now "
229  << m_cWndCnt << " cnd " << cnt);
230  }
231  }
232 }
233 
234 uint32_t
236 {
237  NS_LOG_FUNCTION(this);
238  Time t;
239  uint32_t delta;
240  uint32_t bicTarget;
241  uint32_t cnt = 0;
242  double offs;
243  uint32_t segCwnd = tcb->GetCwndInSegments();
244 
245  if (m_epochStart == Time::Min())
246  {
247  m_epochStart = Simulator::Now(); // record the beginning of an epoch
248 
249  if (m_lastMaxCwnd <= segCwnd)
250  {
251  NS_LOG_DEBUG("lastMaxCwnd <= m_cWnd. K=0 and origin=" << segCwnd);
252  m_bicK = 0.0;
253  m_bicOriginPoint = segCwnd;
254  }
255  else
256  {
257  m_bicK = std::pow((m_lastMaxCwnd - segCwnd) / m_c, 1 / 3.);
259  NS_LOG_DEBUG("lastMaxCwnd > m_cWnd. K=" << m_bicK << " and origin=" << m_lastMaxCwnd);
260  }
261  }
262 
264 
265  if (t.GetSeconds() < m_bicK) /* t - K */
266  {
267  offs = m_bicK - t.GetSeconds();
268  NS_LOG_DEBUG("t=" << t.GetSeconds() << " <k: offs=" << offs);
269  }
270  else
271  {
272  offs = t.GetSeconds() - m_bicK;
273  NS_LOG_DEBUG("t=" << t.GetSeconds() << " >= k: offs=" << offs);
274  }
275 
276  /* Constant value taken from Experimental Evaluation of Cubic Tcp, available at
277  * eprints.nuim.ie/1716/1/Hamiltonpfldnet2007_cubic_final.pdf */
278  delta = m_c * std::pow(offs, 3);
279 
280  NS_LOG_DEBUG("delta: " << delta);
281 
282  if (t.GetSeconds() < m_bicK)
283  {
284  // below origin
285  bicTarget = m_bicOriginPoint - delta;
286  NS_LOG_DEBUG("t < k: Bic Target: " << bicTarget);
287  }
288  else
289  {
290  // above origin
291  bicTarget = m_bicOriginPoint + delta;
292  NS_LOG_DEBUG("t >= k: Bic Target: " << bicTarget);
293  }
294 
295  // Next the window target is converted into a cnt or count value. CUBIC will
296  // wait until enough new ACKs have arrived that a counter meets or exceeds
297  // this cnt value. This is how the CUBIC implementation simulates growing
298  // cwnd by values other than 1 segment size.
299  if (bicTarget > segCwnd)
300  {
301  cnt = segCwnd / (bicTarget - segCwnd);
302  NS_LOG_DEBUG("target>cwnd. cnt=" << cnt);
303  }
304  else
305  {
306  cnt = 100 * segCwnd;
307  }
308 
309  if (m_lastMaxCwnd == 0 && cnt > m_cntClamp)
310  {
311  cnt = m_cntClamp;
312  }
313 
314  // The maximum rate of cwnd increase CUBIC allows is 1 packet per
315  // 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
316  return std::max(cnt, 2U);
317 }
318 
319 void
320 TcpCubic::PktsAcked(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked, const Time& rtt)
321 {
322  NS_LOG_FUNCTION(this << tcb << segmentsAcked << rtt);
323 
324  /* Discard delay samples right after fast recovery */
326  {
327  return;
328  }
329 
330  /* first time call or link delay decreases */
331  if (m_delayMin == Time::Min() || m_delayMin > rtt)
332  {
333  m_delayMin = rtt;
334  }
335 
336  /* hystart triggers when cwnd is larger than some threshold */
337  if (m_hystart && tcb->m_cWnd <= tcb->m_ssThresh &&
338  tcb->m_cWnd >= m_hystartLowWindow * tcb->m_segmentSize)
339  {
340  HystartUpdate(tcb, rtt);
341  }
342 }
343 
344 void
346 {
347  NS_LOG_FUNCTION(this << delay);
348 
349  if (!m_found)
350  {
351  Time now = Simulator::Now();
352 
353  /* first detection parameter - ack-train detection */
354  if ((now - m_lastAck) <= m_hystartAckDelta)
355  {
356  m_lastAck = now;
357 
358  if ((now - m_roundStart) > m_delayMin)
359  {
360  if (m_hystartDetect == HybridSSDetectionMode::PACKET_TRAIN ||
361  m_hystartDetect == HybridSSDetectionMode::BOTH)
362  {
363  m_found = true;
364  }
365  }
366  }
367 
368  /* obtain the minimum delay of more than sampling packets */
370  {
371  if (m_currRtt == Time::Min() || m_currRtt > delay)
372  {
373  m_currRtt = delay;
374  }
375 
376  ++m_sampleCnt;
377  }
379  {
381  m_hystartDetect == HybridSSDetectionMode::BOTH)
382  {
383  m_found = true;
384  }
385  }
386 
387  /*
388  * Either one of two conditions are met,
389  * we exit from slow start immediately.
390  */
391  if (m_found)
392  {
393  NS_LOG_DEBUG("Exit from SS, immediately :-)");
394  tcb->m_ssThresh = tcb->m_cWnd;
395  }
396  }
397 }
398 
399 Time
401 {
402  NS_LOG_FUNCTION(this << t);
403 
404  Time ret = t;
405  if (t > m_hystartDelayMax)
406  {
407  ret = m_hystartDelayMax;
408  }
409  else if (t < m_hystartDelayMin)
410  {
411  ret = m_hystartDelayMin;
412  }
413 
414  return ret;
415 }
416 
417 uint32_t
419 {
420  NS_LOG_FUNCTION(this << tcb << bytesInFlight);
421 
422  uint32_t segCwnd = tcb->GetCwndInSegments();
423  NS_LOG_DEBUG("Loss at cWnd=" << segCwnd
424  << " segments in flight=" << bytesInFlight / tcb->m_segmentSize);
425 
426  /* Wmax and fast convergence */
427  if (segCwnd < m_lastMaxCwnd && m_fastConvergence)
428  {
429  m_lastMaxCwnd = (segCwnd * (1 + m_beta)) / 2; // Section 4.6 in RFC 8312
430  }
431  else
432  {
433  m_lastMaxCwnd = segCwnd;
434  }
435 
436  m_epochStart = Time::Min(); // end of epoch
437 
438  /* Formula taken from the Linux kernel */
439  uint32_t ssThresh = std::max(static_cast<uint32_t>(segCwnd * m_beta), 2U) * tcb->m_segmentSize;
440 
441  NS_LOG_DEBUG("SsThresh = " << ssThresh);
442 
443  return ssThresh;
444 }
445 
446 void
448 {
449  NS_LOG_FUNCTION(this << tcb << newState);
450 
451  if (newState == TcpSocketState::CA_LOSS)
452  {
453  CubicReset(tcb);
454  HystartReset(tcb);
455  }
456 }
457 
458 void
460 {
461  NS_LOG_FUNCTION(this << tcb);
462 
463  m_lastMaxCwnd = 0;
464  m_bicOriginPoint = 0;
465  m_bicK = 0;
466  m_delayMin = Time::Min();
467  m_found = false;
468 }
469 
472 {
473  NS_LOG_FUNCTION(this);
474  return CopyObject<TcpCubic>(this);
475 }
476 
477 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:43
AttributeValue implementation for Boolean.
Definition: boolean.h:37
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Hold variables of type enum.
Definition: enum.h:56
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
Congestion control abstract class.
The Cubic Congestion Control Algorithm.
Definition: tcp-cubic.h:70
Time m_currRtt
Current Rtt.
Definition: tcp-cubic.h:135
void HystartReset(Ptr< const TcpSocketState > tcb)
Reset HyStart parameters.
Definition: tcp-cubic.cc:172
Time m_hystartDelayMax
Maximum time for hystart algorithm.
Definition: tcp-cubic.h:114
Time m_cubicDelta
Time to wait after recovery before update.
Definition: tcp-cubic.h:134
uint32_t m_bicOriginPoint
Origin point of bic function.
Definition: tcp-cubic.h:125
uint32_t Update(Ptr< TcpSocketState > tcb)
Cubic window update after a new ack received.
Definition: tcp-cubic.cc:235
uint32_t m_sampleCnt
Count of samples for HyStart.
Definition: tcp-cubic.h:136
uint32_t GetSsThresh(Ptr< const TcpSocketState > tcb, uint32_t bytesInFlight) override
Get the slow start threshold after a loss event.
Definition: tcp-cubic.cc:418
std::string GetName() const override
Get the name of the congestion control algorithm.
Definition: tcp-cubic.cc:166
Ptr< TcpCongestionOps > Fork() override
Copy the congestion control algorithm across sockets.
Definition: tcp-cubic.cc:471
bool m_hystart
Enable or disable HyStart algorithm.
Definition: tcp-cubic.h:109
double m_bicK
Time to origin point from the beginning.
Definition: tcp-cubic.h:126
void PktsAcked(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked, const Time &rtt) override
Timing information on received ACK.
Definition: tcp-cubic.cc:320
uint32_t m_cWndCnt
cWnd integer-to-float counter
Definition: tcp-cubic.h:123
Time m_hystartDelayMin
Minimum time for hystart algorithm.
Definition: tcp-cubic.h:113
bool m_found
The exit point is found?
Definition: tcp-cubic.h:130
SequenceNumber32 m_endSeq
End sequence of the round.
Definition: tcp-cubic.h:132
void IncreaseWindow(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked) override
Congestion avoidance algorithm implementation.
Definition: tcp-cubic.cc:183
double m_beta
Beta for cubic multiplicative increase.
Definition: tcp-cubic.h:107
Time m_lastAck
Last time when the ACK spacing is close.
Definition: tcp-cubic.h:133
void CongestionStateSet(Ptr< TcpSocketState > tcb, const TcpSocketState::TcpCongState_t newState) override
Trigger events/calculations specific to a congestion state.
Definition: tcp-cubic.cc:447
static TypeId GetTypeId()
Get the type ID.
Definition: tcp-cubic.cc:36
Time m_hystartAckDelta
Spacing between ack's indicating train.
Definition: tcp-cubic.h:112
bool m_fastConvergence
Enable or disable fast convergence algorithm.
Definition: tcp-cubic.h:106
Time m_delayMin
Min delay.
Definition: tcp-cubic.h:128
Time m_roundStart
Beginning of each round.
Definition: tcp-cubic.h:131
Time m_epochStart
Beginning of an epoch.
Definition: tcp-cubic.h:129
HybridSSDetectionMode m_hystartDetect
Detect way for HyStart algorithm.
Definition: tcp-cubic.h:110
uint8_t m_cntClamp
Modulo of the (avoided) float division for cWnd.
Definition: tcp-cubic.h:118
void HystartUpdate(Ptr< TcpSocketState > tcb, const Time &delay)
Update HyStart parameters.
Definition: tcp-cubic.cc:345
double m_c
Cubic Scaling factor.
Definition: tcp-cubic.h:120
void CubicReset(Ptr< const TcpSocketState > tcb)
Reset Cubic parameters.
Definition: tcp-cubic.cc:459
uint32_t m_lastMaxCwnd
Last maximum cWnd.
Definition: tcp-cubic.h:124
Time HystartDelayThresh(const Time &t) const
Clamp time value in a range.
Definition: tcp-cubic.cc:400
uint8_t m_hystartMinSamples
Number of delay samples for detecting the increase of delay.
Definition: tcp-cubic.h:115
uint32_t m_hystartLowWindow
Lower bound cWnd for hybrid slow start (segments)
Definition: tcp-cubic.h:111
A base class for implementation of a stream socket using TCP.
uint32_t m_segmentSize
Segment size.
uint32_t GetCwndInSegments() const
Get cwnd in segments rather than bytes.
TcpCongState_t
Definition of the Congestion state machine.
@ CA_LOSS
CWND was reduced due to RTO timeout or SACK reneging.
SequenceNumber32 m_lastAckedSeq
Last sequence ACKed.
TracedValue< uint32_t > m_cWnd
Congestion window.
TracedValue< uint32_t > m_ssThresh
Slow start threshold.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:402
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:286
AttributeValue implementation for Time.
Definition: nstime.h:1423
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
Hold an unsigned integer type.
Definition: uinteger.h:45
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: boolean.h:86
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: double.h:43
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: enum.h:205
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: nstime.h:1424
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition: uinteger.h:46
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:229
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1348
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeEnumChecker(int v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition: enum.h:163
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535
#define DELAY(time)
Gets the delay between a given time and the current time.