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