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
28NS_LOG_COMPONENT_DEFINE("TcpCubic");
29
30namespace ns3
31{
32
34
35TypeId
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 "1: packet train, 2: delay, 3: both",
66 IntegerValue(3),
68 MakeIntegerChecker<int>(1, 3))
69 .AddAttribute("HyStartMinSamples",
70 "Number of delay samples for detecting the increase of delay",
73 MakeUintegerChecker<uint8_t>())
74 .AddAttribute("HyStartAckDelta",
75 "Spacing between ack's indicating train",
79 .AddAttribute("HyStartDelayMin",
80 "Minimum time for hystart algorithm",
84 .AddAttribute("HyStartDelayMax",
85 "Maximum time for hystart algorithm",
89 .AddAttribute("CubicDelta",
90 "Delta Time to wait after fast recovery before adjusting param",
94 .AddAttribute("CntClamp",
95 "Counter value when no losses are detected (counter is used"
96 " when incrementing cWnd in congestion avoidance, to avoid"
97 " floating point arithmetic). It is the modulo of the (avoided)"
98 " division",
99 UintegerValue(20),
101 MakeUintegerChecker<uint8_t>())
102 .AddAttribute("C",
103 "Cubic Scaling factor",
104 DoubleValue(0.4),
106 MakeDoubleChecker<double>(0.0));
107 return tid;
108}
109
112 m_cWndCnt(0),
113 m_lastMaxCwnd(0),
114 m_bicOriginPoint(0),
115 m_bicK(0.0),
116 m_delayMin(Time::Min()),
117 m_epochStart(Time::Min()),
118 m_found(false),
119 m_roundStart(Time::Min()),
120 m_endSeq(0),
121 m_lastAck(Time::Min()),
122 m_cubicDelta(Time::Min()),
123 m_currRtt(Time::Min()),
124 m_sampleCnt(0)
125{
126 NS_LOG_FUNCTION(this);
127}
128
130 : TcpCongestionOps(sock),
131 m_fastConvergence(sock.m_fastConvergence),
132 m_beta(sock.m_beta),
133 m_hystart(sock.m_hystart),
134 m_hystartDetect(sock.m_hystartDetect),
135 m_hystartLowWindow(sock.m_hystartLowWindow),
136 m_hystartAckDelta(sock.m_hystartAckDelta),
137 m_hystartDelayMin(sock.m_hystartDelayMin),
138 m_hystartDelayMax(sock.m_hystartDelayMax),
139 m_hystartMinSamples(sock.m_hystartMinSamples),
140 m_initialCwnd(sock.m_initialCwnd),
141 m_cntClamp(sock.m_cntClamp),
142 m_c(sock.m_c),
143 m_cWndCnt(sock.m_cWndCnt),
144 m_lastMaxCwnd(sock.m_lastMaxCwnd),
145 m_bicOriginPoint(sock.m_bicOriginPoint),
146 m_bicK(sock.m_bicK),
147 m_delayMin(sock.m_delayMin),
148 m_epochStart(sock.m_epochStart),
149 m_found(sock.m_found),
150 m_roundStart(sock.m_roundStart),
151 m_endSeq(sock.m_endSeq),
152 m_lastAck(sock.m_lastAck),
153 m_cubicDelta(sock.m_cubicDelta),
154 m_currRtt(sock.m_currRtt),
155 m_sampleCnt(sock.m_sampleCnt)
156{
157 NS_LOG_FUNCTION(this);
158}
159
160std::string
162{
163 return "TcpCubic";
164}
165
166void
168{
169 NS_LOG_FUNCTION(this);
170
172 m_endSeq = tcb->m_highTxMark;
174 m_sampleCnt = 0;
175}
176
177void
179{
180 NS_LOG_FUNCTION(this << tcb << segmentsAcked);
181
182 if (tcb->m_cWnd < tcb->m_ssThresh)
183 {
184 if (m_hystart && tcb->m_lastAckedSeq > m_endSeq)
185 {
186 HystartReset(tcb);
187 }
188
189 // In Linux, the QUICKACK socket option enables the receiver to send
190 // immediate acks initially (during slow start) and then transition
191 // to delayed acks. ns-3 does not implement QUICKACK, and if ack
192 // counting instead of byte counting is used during slow start window
193 // growth, when TcpSocket::DelAckCount==2, then the slow start will
194 // not reach as large of an initial window as in Linux. Therefore,
195 // we can approximate the effect of QUICKACK by making this slow
196 // start phase perform Appropriate Byte Counting (RFC 3465)
197 tcb->m_cWnd += segmentsAcked * tcb->m_segmentSize;
198 segmentsAcked = 0;
199
200 NS_LOG_INFO("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh "
201 << tcb->m_ssThresh);
202 }
203
204 if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
205 {
206 m_cWndCnt += segmentsAcked;
207 uint32_t cnt = Update(tcb);
208
209 /* According to RFC 6356 even once the new cwnd is
210 * calculated you must compare this to the number of ACKs received since
211 * the last cwnd update. If not enough ACKs have been received then cwnd
212 * cannot be updated.
213 */
214 if (m_cWndCnt >= cnt)
215 {
216 tcb->m_cWnd += tcb->m_segmentSize;
217 m_cWndCnt -= cnt;
218 NS_LOG_INFO("In CongAvoid, updated to cwnd " << tcb->m_cWnd);
219 }
220 else
221 {
222 NS_LOG_INFO("Not enough segments have been ACKed to increment cwnd."
223 "Until now "
224 << m_cWndCnt << " cnd " << cnt);
225 }
226 }
227}
228
231{
232 NS_LOG_FUNCTION(this);
233 Time t;
234 uint32_t delta;
235 uint32_t bicTarget;
236 uint32_t cnt = 0;
237 double offs;
238 uint32_t segCwnd = tcb->GetCwndInSegments();
239
240 if (m_epochStart == Time::Min())
241 {
242 m_epochStart = Simulator::Now(); // record the beginning of an epoch
243
244 if (m_lastMaxCwnd <= segCwnd)
245 {
246 NS_LOG_DEBUG("lastMaxCwnd <= m_cWnd. K=0 and origin=" << segCwnd);
247 m_bicK = 0.0;
248 m_bicOriginPoint = segCwnd;
249 }
250 else
251 {
252 m_bicK = std::pow((m_lastMaxCwnd - segCwnd) / m_c, 1 / 3.);
254 NS_LOG_DEBUG("lastMaxCwnd > m_cWnd. K=" << m_bicK << " and origin=" << m_lastMaxCwnd);
255 }
256 }
257
259
260 if (t.GetSeconds() < m_bicK) /* t - K */
261 {
262 offs = m_bicK - t.GetSeconds();
263 NS_LOG_DEBUG("t=" << t.GetSeconds() << " <k: offs=" << offs);
264 }
265 else
266 {
267 offs = t.GetSeconds() - m_bicK;
268 NS_LOG_DEBUG("t=" << t.GetSeconds() << " >= k: offs=" << offs);
269 }
270
271 /* Constant value taken from Experimental Evaluation of Cubic Tcp, available at
272 * eprints.nuim.ie/1716/1/Hamiltonpfldnet2007_cubic_final.pdf */
273 delta = m_c * std::pow(offs, 3);
274
275 NS_LOG_DEBUG("delta: " << delta);
276
277 if (t.GetSeconds() < m_bicK)
278 {
279 // below origin
280 bicTarget = m_bicOriginPoint - delta;
281 NS_LOG_DEBUG("t < k: Bic Target: " << bicTarget);
282 }
283 else
284 {
285 // above origin
286 bicTarget = m_bicOriginPoint + delta;
287 NS_LOG_DEBUG("t >= k: Bic Target: " << bicTarget);
288 }
289
290 // Next the window target is converted into a cnt or count value. CUBIC will
291 // wait until enough new ACKs have arrived that a counter meets or exceeds
292 // this cnt value. This is how the CUBIC implementation simulates growing
293 // cwnd by values other than 1 segment size.
294 if (bicTarget > segCwnd)
295 {
296 cnt = segCwnd / (bicTarget - segCwnd);
297 NS_LOG_DEBUG("target>cwnd. cnt=" << cnt);
298 }
299 else
300 {
301 cnt = 100 * segCwnd;
302 }
303
304 if (m_lastMaxCwnd == 0 && cnt > m_cntClamp)
305 {
306 cnt = m_cntClamp;
307 }
308
309 // The maximum rate of cwnd increase CUBIC allows is 1 packet per
310 // 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
311 return std::max(cnt, 2U);
312}
313
314void
316{
317 NS_LOG_FUNCTION(this << tcb << segmentsAcked << rtt);
318
319 /* Discard delay samples right after fast recovery */
321 {
322 return;
323 }
324
325 /* first time call or link delay decreases */
326 if (m_delayMin == Time::Min() || m_delayMin > rtt)
327 {
328 m_delayMin = rtt;
329 }
330
331 /* hystart triggers when cwnd is larger than some threshold */
332 if (m_hystart && tcb->m_cWnd <= tcb->m_ssThresh &&
334 {
335 HystartUpdate(tcb, rtt);
336 }
337}
338
339void
341{
342 NS_LOG_FUNCTION(this << delay);
343
344 if (!(m_found & m_hystartDetect))
345 {
346 Time now = Simulator::Now();
347
348 /* first detection parameter - ack-train detection */
349 if ((now - m_lastAck) <= m_hystartAckDelta)
350 {
351 m_lastAck = now;
352
353 if ((now - m_roundStart) > m_delayMin)
354 {
356 }
357 }
358
359 /* obtain the minimum delay of more than sampling packets */
361 {
362 if (m_currRtt == Time::Min() || m_currRtt > delay)
363 {
364 m_currRtt = delay;
365 }
366
367 ++m_sampleCnt;
368 }
369 else
370 {
372 {
373 m_found |= DELAY;
374 }
375 }
376 /*
377 * Either one of two conditions are met,
378 * we exit from slow start immediately.
379 */
381 {
382 NS_LOG_DEBUG("Exit from SS, immediately :-)");
383 tcb->m_ssThresh = tcb->m_cWnd;
384 }
385 }
386}
387
388Time
390{
391 NS_LOG_FUNCTION(this << t);
392
393 Time ret = t;
394 if (t > m_hystartDelayMax)
395 {
396 ret = m_hystartDelayMax;
397 }
398 else if (t < m_hystartDelayMin)
399 {
400 ret = m_hystartDelayMin;
401 }
402
403 return ret;
404}
405
408{
409 NS_LOG_FUNCTION(this << tcb << bytesInFlight);
410
411 uint32_t segCwnd = tcb->GetCwndInSegments();
412 NS_LOG_DEBUG("Loss at cWnd=" << segCwnd
413 << " segments in flight=" << bytesInFlight / tcb->m_segmentSize);
414
415 /* Wmax and fast convergence */
416 if (segCwnd < m_lastMaxCwnd && m_fastConvergence)
417 {
418 m_lastMaxCwnd = (segCwnd * (1 + m_beta)) / 2; // Section 4.6 in RFC 8312
419 }
420 else
421 {
422 m_lastMaxCwnd = segCwnd;
423 }
424
425 m_epochStart = Time::Min(); // end of epoch
426
427 /* Formula taken from the Linux kernel */
428 uint32_t ssThresh = std::max(static_cast<uint32_t>(segCwnd * m_beta), 2U) * tcb->m_segmentSize;
429
430 NS_LOG_DEBUG("SsThresh = " << ssThresh);
431
432 return ssThresh;
433}
434
435void
437{
438 NS_LOG_FUNCTION(this << tcb << newState);
439
440 if (newState == TcpSocketState::CA_LOSS)
441 {
442 CubicReset(tcb);
443 HystartReset(tcb);
444 }
445}
446
447void
449{
450 NS_LOG_FUNCTION(this << tcb);
451
452 m_lastMaxCwnd = 0;
454 m_bicK = 0;
456 m_found = false;
457}
458
461{
462 NS_LOG_FUNCTION(this);
463 return CopyObject<TcpCubic>(this);
464}
465
466} // 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 a signed integer type.
Definition: integer.h:45
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:134
void HystartReset(Ptr< const TcpSocketState > tcb)
Reset HyStart parameters.
Definition: tcp-cubic.cc:167
Time m_hystartDelayMax
Maximum time for hystart algorithm.
Definition: tcp-cubic.h:113
Time m_cubicDelta
Time to wait after recovery before update.
Definition: tcp-cubic.h:133
uint32_t m_bicOriginPoint
Origin point of bic function.
Definition: tcp-cubic.h:124
uint32_t Update(Ptr< TcpSocketState > tcb)
Cubic window update after a new ack received.
Definition: tcp-cubic.cc:230
uint32_t m_sampleCnt
Count of samples for HyStart.
Definition: tcp-cubic.h:135
uint32_t GetSsThresh(Ptr< const TcpSocketState > tcb, uint32_t bytesInFlight) override
Get the slow start threshold after a loss event.
Definition: tcp-cubic.cc:407
std::string GetName() const override
Get the name of the congestion control algorithm.
Definition: tcp-cubic.cc:161
Ptr< TcpCongestionOps > Fork() override
Copy the congestion control algorithm across sockets.
Definition: tcp-cubic.cc:460
bool m_hystart
Enable or disable HyStart algorithm.
Definition: tcp-cubic.h:108
double m_bicK
Time to origin point from the beginning.
Definition: tcp-cubic.h:125
int m_hystartDetect
Detect way for HyStart algorithm.
Definition: tcp-cubic.h:109
void PktsAcked(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked, const Time &rtt) override
Timing information on received ACK.
Definition: tcp-cubic.cc:315
uint32_t m_cWndCnt
cWnd integer-to-float counter
Definition: tcp-cubic.h:122
Time m_hystartDelayMin
Minimum time for hystart algorithm.
Definition: tcp-cubic.h:112
bool m_found
The exit point is found?
Definition: tcp-cubic.h:129
SequenceNumber32 m_endSeq
End sequence of the round.
Definition: tcp-cubic.h:131
void IncreaseWindow(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked) override
Congestion avoidance algorithm implementation.
Definition: tcp-cubic.cc:178
double m_beta
Beta for cubic multiplicative increase.
Definition: tcp-cubic.h:106
Time m_lastAck
Last time when the ACK spacing is close.
Definition: tcp-cubic.h:132
void CongestionStateSet(Ptr< TcpSocketState > tcb, const TcpSocketState::TcpCongState_t newState) override
Trigger events/calculations specific to a congestion state.
Definition: tcp-cubic.cc:436
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:111
bool m_fastConvergence
Enable or disable fast convergence algorithm.
Definition: tcp-cubic.h:105
Time m_delayMin
Min delay.
Definition: tcp-cubic.h:127
Time m_roundStart
Beginning of each round.
Definition: tcp-cubic.h:130
Time m_epochStart
Beginning of an epoch.
Definition: tcp-cubic.h:128
uint8_t m_cntClamp
Modulo of the (avoided) float division for cWnd.
Definition: tcp-cubic.h:117
void HystartUpdate(Ptr< TcpSocketState > tcb, const Time &delay)
Update HyStart parameters.
Definition: tcp-cubic.cc:340
double m_c
Cubic Scaling factor.
Definition: tcp-cubic.h:119
void CubicReset(Ptr< const TcpSocketState > tcb)
Reset Cubic parameters.
Definition: tcp-cubic.cc:448
@ DELAY
Detection by delay value.
Definition: tcp-cubic.h:102
@ PACKET_TRAIN
Detection by trains of packet.
Definition: tcp-cubic.h:101
uint32_t m_lastMaxCwnd
Last maximum cWnd.
Definition: tcp-cubic.h:123
Time HystartDelayThresh(const Time &t) const
Clamp time value in a range.
Definition: tcp-cubic.cc:389
uint8_t m_hystartMinSamples
Number of delay samples for detecting the increase of delay.
Definition: tcp-cubic.h:114
uint32_t m_hystartLowWindow
Lower bound cWnd for hybrid slow start (segments)
Definition: tcp-cubic.h:110
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:1425
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)
Definition: boolean.h:86
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeAccessor > MakeIntegerAccessor(T1 a1)
Definition: integer.h:46
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1426
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
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:45
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1350
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:535