Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(529)

Side by Side Diff: net/quic/core/congestion_control/cubic_bytes_test.cc

Issue 2550453002: Fixed integer signing bug in cubic bytes/packet implementation (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/quic/core/congestion_control/cubic_bytes.h" 5 #include "net/quic/core/congestion_control/cubic_bytes.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "net/quic/core/quic_flags.h" 8 #include "net/quic/core/quic_flags.h"
9 #include "net/quic/test_tools/mock_clock.h" 9 #include "net/quic/test_tools/mock_clock.h"
10 #include "testing/gtest/include/gtest/gtest.h" 10 #include "testing/gtest/include/gtest/gtest.h"
11 11
12 namespace net { 12 namespace net {
13 namespace test { 13 namespace test {
14 14
15 const float kBeta = 0.7f; // Default Cubic backoff factor. 15 const float kBeta = 0.7f; // Default Cubic backoff factor.
16 const uint32_t kNumConnections = 2; 16 const uint32_t kNumConnections = 2;
17 const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections; 17 const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
18 const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections * 18 const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
19 (1 - kNConnectionBeta) / (1 + kNConnectionBeta); 19 (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
20 20
21 class CubicBytesTest : public ::testing::Test { 21 class CubicBytesTest : public ::testing::TestWithParam<bool> {
22 protected: 22 protected:
23 CubicBytesTest() 23 CubicBytesTest()
24 : one_ms_(QuicTime::Delta::FromMilliseconds(1)), 24 : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
25 hundred_ms_(QuicTime::Delta::FromMilliseconds(100)), 25 hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
26 cubic_(&clock_) {} 26 cubic_(&clock_),
27 fix_convex_mode_(GetParam()) {
28 cubic_.SetFixConvexMode(fix_convex_mode_);
29 }
30
31 QuicByteCount RenoCwndInBytes(QuicByteCount current_cwnd) {
32 QuicByteCount reno_estimated_cwnd =
33 current_cwnd +
34 kDefaultTCPMSS * (kNConnectionAlpha * kDefaultTCPMSS) / current_cwnd;
35 return reno_estimated_cwnd;
36 }
37
38 QuicByteCount ConservativeCwndInBytes(QuicByteCount current_cwnd) {
39 QuicByteCount conservative_cwnd = current_cwnd + kDefaultTCPMSS / 2;
40 return conservative_cwnd;
41 }
42
43 QuicByteCount CubicConvexCwndInBytes(QuicByteCount initial_cwnd,
44 int64_t rtt_ms,
45 int64_t elapsed_time_ms) {
46 const int64_t offset = ((elapsed_time_ms + rtt_ms) << 10) / 1000;
47 const QuicByteCount delta_congestion_window =
48 ((410 * offset * offset * offset) >> 40) * kDefaultTCPMSS;
49 const QuicByteCount cubic_cwnd = initial_cwnd + delta_congestion_window;
50 return cubic_cwnd;
51 }
52
27 const QuicTime::Delta one_ms_; 53 const QuicTime::Delta one_ms_;
28 const QuicTime::Delta hundred_ms_; 54 const QuicTime::Delta hundred_ms_;
29 MockClock clock_; 55 MockClock clock_;
30 CubicBytes cubic_; 56 CubicBytes cubic_;
57 bool fix_convex_mode_;
31 }; 58 };
32 59
33 TEST_F(CubicBytesTest, AboveOrigin) { 60 INSTANTIATE_TEST_CASE_P(CubicBytesTests, CubicBytesTest, testing::Bool());
61
62 // TODO(jokulik): The original "AboveOrigin" test, below, is very
63 // loose. It's nearly impossible to make the test tighter without
64 // deploying the fix for convex mode. Once cubic convex is deployed,
65 // replace "AboveOrigin" with this test.
66 TEST_P(CubicBytesTest, AboveOriginWithTighterBounds) {
67 if (!fix_convex_mode_) {
68 // Without convex mode fixed, the behavior of the algorithm is so
69 // far from expected, there's no point in doing a tighter test.
70 return;
71 }
72 // Convex growth.
73 const QuicTime::Delta rtt_min = hundred_ms_;
74 int64_t rtt_min_ms = rtt_min.ToMilliseconds();
75 float rtt_min_s = rtt_min_ms / 1000.0;
76 QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
77 const QuicByteCount initial_cwnd = current_cwnd;
78
79 clock_.AdvanceTime(one_ms_);
80 const QuicTime initial_time = clock_.ApproximateNow();
81 const QuicByteCount expected_first_cwnd = RenoCwndInBytes(current_cwnd);
82 current_cwnd =
83 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
84 ASSERT_EQ(expected_first_cwnd, current_cwnd);
85
86 // Normal TCP phase.
87 // The maximum number of expected Reno RTTs is calculated by
88 // finding the point where the cubic curve and the reno curve meet.
89 const int max_reno_rtts =
90 std::sqrt(kNConnectionAlpha / (.4 * rtt_min_s * rtt_min_s * rtt_min_s)) -
91 1;
92 for (int i = 0; i < max_reno_rtts; ++i) {
93 // Alternatively, we expect it to increase by one, every time we
94 // receive current_cwnd/Alpha acks back. (This is another way of
95 // saying we expect cwnd to increase by approximately Alpha once
96 // we receive current_cwnd number ofacks back).
97 const uint64_t num_acks_this_epoch =
98 current_cwnd / kDefaultTCPMSS / kNConnectionAlpha;
99 const QuicByteCount initial_cwnd_this_epoch = current_cwnd;
100 for (QuicPacketCount n = 0; n < num_acks_this_epoch; ++n) {
101 // Call once per ACK.
102 const QuicByteCount expected_next_cwnd = RenoCwndInBytes(current_cwnd);
103 current_cwnd = cubic_.CongestionWindowAfterAck(kDefaultTCPMSS,
104 current_cwnd, rtt_min);
105 ASSERT_EQ(expected_next_cwnd, current_cwnd);
106 }
107 // Our byte-wise Reno implementation is an estimate. We expect
108 // the cwnd to increase by approximately one MSS every
109 // cwnd/kDefaultTCPMSS/Alpha acks, but it may be off by as much as
110 // half a packet for smaller values of current_cwnd.
111 const QuicByteCount cwnd_change_this_epoch =
112 current_cwnd - initial_cwnd_this_epoch;
113 ASSERT_NEAR(kDefaultTCPMSS, cwnd_change_this_epoch, kDefaultTCPMSS / 2);
114 clock_.AdvanceTime(hundred_ms_);
115 }
116
117 // Because our byte-wise Reno under-estimates the cwnd, we switch to
118 // conservative increases for a few acks before switching to true
119 // cubic increases.
120 for (int i = 0; i < 3; ++i) {
121 const QuicByteCount next_expected_cwnd =
122 ConservativeCwndInBytes(current_cwnd);
123 current_cwnd =
124 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
125 ASSERT_EQ(next_expected_cwnd, current_cwnd);
126 }
127
128 for (int i = 0; i < 54; ++i) {
129 const uint64_t max_acks_this_epoch = current_cwnd / kDefaultTCPMSS;
130 const int elapsed_time_ms =
131 (clock_.ApproximateNow() - initial_time).ToMilliseconds();
132 const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
133 initial_cwnd, rtt_min.ToMilliseconds(), elapsed_time_ms);
134 current_cwnd =
135 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
136 ASSERT_EQ(expected_cwnd, current_cwnd);
137
138 for (QuicPacketCount n = 1; n < max_acks_this_epoch; ++n) {
139 // Call once per ACK.
140 ASSERT_EQ(current_cwnd, cubic_.CongestionWindowAfterAck(
141 kDefaultTCPMSS, current_cwnd, rtt_min));
142 }
143 clock_.AdvanceTime(hundred_ms_);
144 }
145 const int elapsed_time_ms =
146 (clock_.ApproximateNow() - initial_time).ToMilliseconds();
147 const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
148 initial_cwnd, rtt_min.ToMilliseconds(), elapsed_time_ms);
149 current_cwnd =
150 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
151 ASSERT_EQ(expected_cwnd, current_cwnd);
152 }
153
154 TEST_P(CubicBytesTest, AboveOrigin) {
34 // Convex growth. 155 // Convex growth.
35 const QuicTime::Delta rtt_min = hundred_ms_; 156 const QuicTime::Delta rtt_min = hundred_ms_;
36 QuicByteCount current_cwnd = 10 * kDefaultTCPMSS; 157 QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
37 QuicByteCount expected_cwnd = current_cwnd + (kDefaultTCPMSS / 2); 158 // Without the signed-integer, cubic-convex fix, we start out in the
159 // wrong mode.
160 QuicPacketCount expected_cwnd = fix_convex_mode_
161 ? RenoCwndInBytes(current_cwnd)
162 : ConservativeCwndInBytes(current_cwnd);
38 // Initialize the state. 163 // Initialize the state.
39 clock_.AdvanceTime(one_ms_); 164 clock_.AdvanceTime(one_ms_);
40 EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck( 165 ASSERT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
41 kDefaultTCPMSS, current_cwnd, rtt_min)); 166 kDefaultTCPMSS, current_cwnd, rtt_min));
42 current_cwnd = expected_cwnd; 167 current_cwnd = expected_cwnd;
168 const QuicPacketCount initial_cwnd = expected_cwnd;
43 // Normal TCP phase. 169 // Normal TCP phase.
44 for (int i = 0; i < 48; ++i) { 170 for (int i = 0; i < 48; ++i) {
45 for (QuicPacketCount n = 1; 171 for (QuicPacketCount n = 1;
46 n < current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; ++n) { 172 n < current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; ++n) {
47 // Call once per ACK. 173 // Call once per ACK.
48 EXPECT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck( 174 ASSERT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
49 kDefaultTCPMSS, current_cwnd, rtt_min), 175 kDefaultTCPMSS, current_cwnd, rtt_min),
50 kDefaultTCPMSS); 176 kDefaultTCPMSS);
51 } 177 }
52 clock_.AdvanceTime(hundred_ms_); 178 clock_.AdvanceTime(hundred_ms_);
53 current_cwnd = 179 current_cwnd =
54 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min); 180 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
55 EXPECT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS); 181 if (fix_convex_mode_) {
56 expected_cwnd += kDefaultTCPMSS; 182 // When we fix convex mode and the uint64 arithmetic, we
183 // increase the expected_cwnd only after after the first 100ms,
184 // rather than after the initial 1ms.
185 expected_cwnd += kDefaultTCPMSS;
186 ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
187 } else {
188 ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
189 expected_cwnd += kDefaultTCPMSS;
190 }
57 } 191 }
58 // Cubic phase. 192 // Cubic phase.
59 for (int i = 0; i < 52; ++i) { 193 for (int i = 0; i < 52; ++i) {
60 for (QuicPacketCount n = 1; n < current_cwnd / kDefaultTCPMSS; ++n) { 194 for (QuicPacketCount n = 1; n < current_cwnd / kDefaultTCPMSS; ++n) {
61 // Call once per ACK. 195 // Call once per ACK.
62 EXPECT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck( 196 ASSERT_NEAR(current_cwnd, cubic_.CongestionWindowAfterAck(
63 kDefaultTCPMSS, current_cwnd, rtt_min), 197 kDefaultTCPMSS, current_cwnd, rtt_min),
64 kDefaultTCPMSS); 198 kDefaultTCPMSS);
65 } 199 }
66 clock_.AdvanceTime(hundred_ms_); 200 clock_.AdvanceTime(hundred_ms_);
67 current_cwnd = 201 current_cwnd =
68 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min); 202 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
69 } 203 }
70 // Total time elapsed so far; add min_rtt (0.1s) here as well. 204 // Total time elapsed so far; add min_rtt (0.1s) here as well.
71 float elapsed_time_s = 10.0f + 0.1f; 205 float elapsed_time_s = 10.0f + 0.1f;
72 // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4. 206 // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
73 expected_cwnd = 207 expected_cwnd =
74 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024; 208 initial_cwnd / kDefaultTCPMSS +
209 (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
210 // Without the convex mode fix, the result is off by one.
211 if (!fix_convex_mode_) {
212 ++expected_cwnd;
213 }
75 EXPECT_EQ(expected_cwnd, current_cwnd / kDefaultTCPMSS); 214 EXPECT_EQ(expected_cwnd, current_cwnd / kDefaultTCPMSS);
76 } 215 }
77 216
78 TEST_F(CubicBytesTest, LossEvents) { 217 TEST_P(CubicBytesTest, LossEvents) {
79 const QuicTime::Delta rtt_min = hundred_ms_; 218 const QuicTime::Delta rtt_min = hundred_ms_;
80 QuicByteCount current_cwnd = 422 * kDefaultTCPMSS; 219 QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
81 QuicPacketCount expected_cwnd = current_cwnd + kDefaultTCPMSS / 2; 220 // Without the signed-integer, cubic-convex fix, we mistakenly
221 // increment cwnd after only one_ms_ and a single ack.
222 QuicPacketCount expected_cwnd = fix_convex_mode_
223 ? RenoCwndInBytes(current_cwnd)
224 : current_cwnd + kDefaultTCPMSS / 2;
82 // Initialize the state. 225 // Initialize the state.
83 clock_.AdvanceTime(one_ms_); 226 clock_.AdvanceTime(one_ms_);
84 EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck( 227 EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
85 kDefaultTCPMSS, current_cwnd, rtt_min)); 228 kDefaultTCPMSS, current_cwnd, rtt_min));
86 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta); 229 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta);
87 EXPECT_EQ(expected_cwnd, 230 EXPECT_EQ(expected_cwnd,
88 cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); 231 cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
89 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta); 232 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta);
90 EXPECT_EQ(expected_cwnd, 233 EXPECT_EQ(expected_cwnd,
91 cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); 234 cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
92 } 235 }
93 236
94 TEST_F(CubicBytesTest, BelowOrigin) { 237 TEST_P(CubicBytesTest, BelowOrigin) {
95 // Concave growth. 238 // Concave growth.
96 const QuicTime::Delta rtt_min = hundred_ms_; 239 const QuicTime::Delta rtt_min = hundred_ms_;
97 QuicByteCount current_cwnd = 422 * kDefaultTCPMSS; 240 QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
98 QuicPacketCount expected_cwnd = current_cwnd + kDefaultTCPMSS / 2; 241 // Without the signed-integer, cubic-convex fix, we mistakenly
242 // increment cwnd after only one_ms_ and a single ack.
243 QuicPacketCount expected_cwnd = fix_convex_mode_
244 ? RenoCwndInBytes(current_cwnd)
245 : current_cwnd + kDefaultTCPMSS / 2;
99 // Initialize the state. 246 // Initialize the state.
100 clock_.AdvanceTime(one_ms_); 247 clock_.AdvanceTime(one_ms_);
101 EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck( 248 EXPECT_EQ(expected_cwnd, cubic_.CongestionWindowAfterAck(
102 kDefaultTCPMSS, current_cwnd, rtt_min)); 249 kDefaultTCPMSS, current_cwnd, rtt_min));
103 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta); 250 expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta);
104 EXPECT_EQ(expected_cwnd, 251 EXPECT_EQ(expected_cwnd,
105 cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); 252 cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
106 current_cwnd = expected_cwnd; 253 current_cwnd = expected_cwnd;
107 // First update after loss to initialize the epoch. 254 // First update after loss to initialize the epoch.
108 current_cwnd = 255 current_cwnd =
109 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min); 256 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
110 // Cubic phase. 257 // Cubic phase.
111 for (int i = 0; i < 40; ++i) { 258 for (int i = 0; i < 40; ++i) {
112 clock_.AdvanceTime(hundred_ms_); 259 clock_.AdvanceTime(hundred_ms_);
113 current_cwnd = 260 current_cwnd =
114 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min); 261 cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min);
115 } 262 }
116 expected_cwnd = 553632; 263 expected_cwnd = 553632;
117 EXPECT_EQ(expected_cwnd, current_cwnd); 264 EXPECT_EQ(expected_cwnd, current_cwnd);
118 } 265 }
119 266
120 } // namespace test 267 } // namespace test
121 } // namespace net 268 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698