Throughput: Change the min number of requests in flight required
In network quality estimator (NQE), change the minimum number of
requests in flight required before a throughput observation can be
taken. The parameter is controlled using field trial params, with
default value set to 1.
BUG=725146
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.android:android_cronet_tester
Review-Url: https://codereview.chromium.org/2893933004
Cr-Commit-Position: refs/heads/master@{#473755}
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 1728675..6b9be03 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -319,7 +319,7 @@
AddDefaultEstimates();
throughput_analyzer_.reset(new nqe::internal::ThroughputAnalyzer(
- base::ThreadTaskRunnerHandle::Get(),
+ ¶ms_, base::ThreadTaskRunnerHandle::Get(),
base::Bind(&NetworkQualityEstimator::OnNewThroughputObservationAvailable,
base::Unretained(this)),
use_localhost_requests_, use_smaller_responses_for_tests));
diff --git a/net/nqe/network_quality_estimator_params.cc b/net/nqe/network_quality_estimator_params.cc
index 7372ba5b..ab4dc9c2 100644
--- a/net/nqe/network_quality_estimator_params.cc
+++ b/net/nqe/network_quality_estimator_params.cc
@@ -368,6 +368,10 @@
NetworkQualityEstimatorParams::NetworkQualityEstimatorParams(
const std::map<std::string, std::string>& params)
: params_(params),
+ throughput_min_requests_in_flight_(
+ GetValueForVariationParam(params_,
+ "throughput_min_requests_in_flight",
+ 1)),
weight_multiplier_per_second_(GetWeightMultiplierPerSecond(params_)),
weight_multiplier_per_dbm_(
GetDoubleValueForVariationParamWithDefaultValue(params_,
diff --git a/net/nqe/network_quality_estimator_params.h b/net/nqe/network_quality_estimator_params.h
index 5c279e1..f543a54 100644
--- a/net/nqe/network_quality_estimator_params.h
+++ b/net/nqe/network_quality_estimator_params.h
@@ -60,6 +60,13 @@
// Returns the threshold for effective connection type |type|.
const NetworkQuality& ConnectionThreshold(EffectiveConnectionType type) const;
+ // Returns the minimum number of requests in-flight to consider the network
+ // fully utilized. A throughput observation is taken only when the network is
+ // considered as fully utilized.
+ size_t throughput_min_requests_in_flight() const {
+ return throughput_min_requests_in_flight_;
+ }
+
// Returns the weight multiplier per second, which represents the factor by
// which the weight of an observation reduces every second.
double weight_multiplier_per_second() const {
@@ -103,6 +110,7 @@
// NetworkQualityEstimator field trial.
const std::map<std::string, std::string> params_;
+ const size_t throughput_min_requests_in_flight_;
const double weight_multiplier_per_second_;
const double weight_multiplier_per_dbm_;
const double correlation_uma_logging_probability_;
diff --git a/net/nqe/throughput_analyzer.cc b/net/nqe/throughput_analyzer.cc
index 6785d14..de1b0ef 100644
--- a/net/nqe/throughput_analyzer.cc
+++ b/net/nqe/throughput_analyzer.cc
@@ -10,6 +10,7 @@
#include "base/single_thread_task_runner.h"
#include "net/base/network_activity_monitor.h"
#include "net/base/url_util.h"
+#include "net/nqe/network_quality_estimator_params.h"
#include "net/url_request/url_request.h"
#if defined(OS_ANDROID)
@@ -35,11 +36,13 @@
namespace internal {
ThroughputAnalyzer::ThroughputAnalyzer(
+ const NetworkQualityEstimatorParams* params,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
ThroughputObservationCallback throughput_observation_callback,
bool use_local_host_requests_for_tests,
bool use_smaller_responses_for_tests)
- : task_runner_(task_runner),
+ : params_(params),
+ task_runner_(task_runner),
throughput_observation_callback_(throughput_observation_callback),
last_connection_change_(base::TimeTicks::Now()),
window_start_time_(base::TimeTicks()),
@@ -47,6 +50,7 @@
disable_throughput_measurements_(false),
use_localhost_requests_for_tests_(use_local_host_requests_for_tests),
use_small_responses_for_tests_(use_smaller_responses_for_tests) {
+ DCHECK(params_);
DCHECK(task_runner_);
DCHECK(!IsCurrentlyTrackingThroughput());
}
@@ -66,7 +70,8 @@
// started, and there is at least one active request that does not degrade
// throughput computation accuracy.
if (accuracy_degrading_requests_.size() > 0 ||
- IsCurrentlyTrackingThroughput() || requests_.size() <= 0) {
+ IsCurrentlyTrackingThroughput() ||
+ requests_.size() < params_->throughput_min_requests_in_flight()) {
return;
}
window_start_time_ = base::TimeTicks::Now();
@@ -96,6 +101,8 @@
// requests should be currently active.
DCHECK_EQ(0U, accuracy_degrading_requests_.size());
+ DCHECK_LE(params_->throughput_min_requests_in_flight(), requests_.size());
+
return true;
}
@@ -141,7 +148,7 @@
}
int32_t downstream_kbps;
- if (MayBeGetThroughputObservation(&downstream_kbps)) {
+ if (MaybeGetThroughputObservation(&downstream_kbps)) {
// Notify the provided callback.
task_runner_->PostTask(
FROM_HERE,
@@ -164,7 +171,7 @@
if (requests_.erase(&request) == 1u) {
// If there is no network activity, stop tracking throughput to prevent
// recording of any observations.
- if (requests_.size() == 0)
+ if (requests_.size() < params_->throughput_min_requests_in_flight())
EndThroughputObservationWindow();
return;
}
@@ -172,7 +179,7 @@
NOTREACHED();
}
-bool ThroughputAnalyzer::MayBeGetThroughputObservation(
+bool ThroughputAnalyzer::MaybeGetThroughputObservation(
int32_t* downstream_kbps) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(downstream_kbps);
diff --git a/net/nqe/throughput_analyzer.h b/net/nqe/throughput_analyzer.h
index e94102b..d83e9db 100644
--- a/net/nqe/throughput_analyzer.h
+++ b/net/nqe/throughput_analyzer.h
@@ -31,6 +31,8 @@
namespace internal {
+class NetworkQualityEstimatorParams;
+
// Makes throughput observations. Polls NetworkActivityMonitor
// (TrafficStats on Android) to count number of bits received over throughput
// observation windows in accordance with the following rules:
@@ -55,6 +57,7 @@
// estimation.
// Virtualized for testing.
ThroughputAnalyzer(
+ const NetworkQualityEstimatorParams* params,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
ThroughputObservationCallback throughput_observation_callback,
bool use_local_host_requests_for_tests,
@@ -106,7 +109,7 @@
// tracking throughput. A throughput observation can be taken only if the
// time-window is currently active, and enough bytes have accumulated in
// that window. |downstream_kbps| should not be null.
- bool MayBeGetThroughputObservation(int32_t* downstream_kbps);
+ bool MaybeGetThroughputObservation(int32_t* downstream_kbps);
// Starts the throughput observation window that keeps track of network
// bytes if the following conditions are true:
@@ -133,6 +136,7 @@
// do not exceed their capacities.
void BoundRequestsSize();
+ const NetworkQualityEstimatorParams* params_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Called every time a new throughput observation is available.
diff --git a/net/nqe/throughput_analyzer_unittest.cc b/net/nqe/throughput_analyzer_unittest.cc
index 3792700..4ed98ee 100644
--- a/net/nqe/throughput_analyzer_unittest.cc
+++ b/net/nqe/throughput_analyzer_unittest.cc
@@ -7,7 +7,9 @@
#include <stdint.h>
#include <deque>
+#include <map>
#include <memory>
+#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -15,8 +17,10 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/url_util.h"
+#include "net/nqe/network_quality_estimator_params.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
@@ -30,8 +34,10 @@
class TestThroughputAnalyzer : public internal::ThroughputAnalyzer {
public:
- TestThroughputAnalyzer()
+ explicit TestThroughputAnalyzer(
+ internal::NetworkQualityEstimatorParams* params)
: internal::ThroughputAnalyzer(
+ params,
base::ThreadTaskRunnerHandle::Get(),
base::Bind(
&TestThroughputAnalyzer::OnNewThroughputObservationAvailable,
@@ -78,7 +84,9 @@
}};
for (const auto& test : tests) {
- TestThroughputAnalyzer throughput_analyzer;
+ std::map<std::string, std::string> variation_params;
+ internal::NetworkQualityEstimatorParams params(variation_params);
+ TestThroughputAnalyzer throughput_analyzer(¶ms);
TestDelegate test_delegate;
TestURLRequestContext context;
@@ -128,7 +136,9 @@
for (const auto& test : tests) {
// Localhost requests are not allowed for estimation purposes.
- TestThroughputAnalyzer throughput_analyzer;
+ std::map<std::string, std::string> variation_params;
+ internal::NetworkQualityEstimatorParams params(variation_params);
+ TestThroughputAnalyzer throughput_analyzer(¶ms);
TestDelegate test_delegate;
TestURLRequestContext context;
@@ -186,47 +196,71 @@
// requests overlap.
TEST(ThroughputAnalyzerTest, TestThroughputWithNetworkRequestsOverlap) {
static const struct {
+ size_t throughput_min_requests_in_flight;
+ size_t number_requests_in_flight;
int64_t increment_bits;
bool expect_throughput_observation;
} tests[] = {
{
- 100 * 1000 * 8, true,
+ 1, 2, 100 * 1000 * 8, true,
},
{
- 1, false,
+ 3, 1, 100 * 1000 * 8, false,
+ },
+ {
+ 3, 2, 100 * 1000 * 8, false,
+ },
+ {
+ 3, 3, 100 * 1000 * 8, true,
+ },
+ {
+ 3, 4, 100 * 1000 * 8, true,
+ },
+ {
+ 1, 2, 1, false,
},
};
for (const auto& test : tests) {
// Localhost requests are not allowed for estimation purposes.
- TestThroughputAnalyzer throughput_analyzer;
+ std::map<std::string, std::string> variation_params;
+ variation_params["throughput_min_requests_in_flight"] =
+ base::IntToString(test.throughput_min_requests_in_flight);
+ internal::NetworkQualityEstimatorParams params(variation_params);
+ TestThroughputAnalyzer throughput_analyzer(¶ms);
TestDelegate test_delegate;
TestURLRequestContext context;
EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
- std::unique_ptr<URLRequest> request_network_1 = context.CreateRequest(
- GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
- TRAFFIC_ANNOTATION_FOR_TESTS);
- std::unique_ptr<URLRequest> request_network_2 = context.CreateRequest(
- GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
- TRAFFIC_ANNOTATION_FOR_TESTS);
- request_network_1->Start();
- request_network_2->Start();
+ std::vector<std::unique_ptr<URLRequest>> requests_in_flight;
+
+ for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+ std::unique_ptr<URLRequest> request_network_1 = context.CreateRequest(
+ GURL("http://example.com/echo.html"), DEFAULT_PRIORITY,
+ &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+ requests_in_flight.push_back(std::move(request_network_1));
+ requests_in_flight.back()->Start();
+ }
base::RunLoop().Run();
- EXPECT_LE(0, throughput_analyzer.throughput_observations_received());
+ EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
- throughput_analyzer.NotifyStartTransaction(*request_network_1);
- throughput_analyzer.NotifyStartTransaction(*request_network_2);
+ for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+ URLRequest* request = requests_in_flight.at(i).get();
+ throughput_analyzer.NotifyStartTransaction(*request);
+ }
// Increment the bytes received count to emulate the bytes received for
// |request_network_1| and |request_network_2|.
throughput_analyzer.IncrementBitsReceived(test.increment_bits);
- throughput_analyzer.NotifyRequestCompleted(*request_network_1);
- throughput_analyzer.NotifyRequestCompleted(*request_network_2);
+ for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
+ URLRequest* request = requests_in_flight.at(i).get();
+ throughput_analyzer.NotifyRequestCompleted(*request);
+ }
+
base::RunLoop().RunUntilIdle();
// Only one observation should be taken since two requests overlap.
@@ -238,6 +272,73 @@
}
}
+// Tests if the throughput observation is taken correctly when the start and end
+// of network requests overlap, and the minimum number of in flight requests
+// when taking an observation is more than 1.
+TEST(ThroughputAnalyzerTest, TestThroughputWithMultipleNetworkRequests) {
+ std::map<std::string, std::string> variation_params;
+ variation_params["throughput_min_requests_in_flight"] = "3";
+ internal::NetworkQualityEstimatorParams params(variation_params);
+ TestThroughputAnalyzer throughput_analyzer(¶ms);
+ TestDelegate test_delegate;
+ TestURLRequestContext context;
+
+ EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+ std::unique_ptr<URLRequest> request_1 = context.CreateRequest(
+ GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<URLRequest> request_2 = context.CreateRequest(
+ GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<URLRequest> request_3 = context.CreateRequest(
+ GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<URLRequest> request_4 = context.CreateRequest(
+ GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ request_1->Start();
+ request_2->Start();
+ request_3->Start();
+ request_4->Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+ throughput_analyzer.NotifyStartTransaction(*(request_1.get()));
+ throughput_analyzer.NotifyStartTransaction(*(request_2.get()));
+
+ const size_t increment_bits = 100 * 1000 * 8;
+
+ // Increment the bytes received count to emulate the bytes received for
+ // |request_1| and |request_2|.
+ throughput_analyzer.IncrementBitsReceived(increment_bits);
+
+ throughput_analyzer.NotifyRequestCompleted(*(request_1.get()));
+ base::RunLoop().RunUntilIdle();
+ // No observation should be taken since only 1 request is in flight.
+ EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+ throughput_analyzer.NotifyStartTransaction(*(request_3.get()));
+ throughput_analyzer.NotifyStartTransaction(*(request_4.get()));
+ EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
+
+ // 3 requests are in flight which is at least as many as the minimum number of
+ // in flight requests required. An observation should be taken.
+ throughput_analyzer.IncrementBitsReceived(increment_bits);
+
+ // Only one observation should be taken since two requests overlap.
+ throughput_analyzer.NotifyRequestCompleted(*(request_2.get()));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
+ throughput_analyzer.NotifyRequestCompleted(*(request_3.get()));
+ throughput_analyzer.NotifyRequestCompleted(*(request_4.get()));
+ EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
+}
+
} // namespace
} // namespace nqe