bug 819734 - Token Bucket for Network Requests [a/b test] r=honzab

This commit is contained in:
Patrick McManus 2013-04-15 09:41:27 -04:00
parent 5484ffe8e1
commit 9912596978
13 changed files with 293 additions and 43 deletions

View File

@ -94,7 +94,7 @@
// so we can associate the document URI with the load group.
// until this point, we have an evil hack:
#include "nsIHttpChannelInternal.h"
#include "nsPILoadGroupInternal.h"
// Local Includes
#include "nsDocShellLoadInfo.h"
@ -6548,10 +6548,15 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
if (timingChannel) {
TimeStamp channelCreationTime;
rv = timingChannel->GetChannelCreation(&channelCreationTime);
if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull())
if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
Telemetry::AccumulateTimeDelta(
Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
channelCreationTime);
nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
do_QueryInterface(mLoadGroup);
if (internalLoadGroup)
internalLoadGroup->OnEndPageLoad(aChannel);
}
}
// Timing is picked up by the window, we don't need it anymore

View File

@ -1003,7 +1003,13 @@ pref("network.http.spdy.send-buffer-size", 131072);
pref("network.http.diagnostics", false);
#ifdef RELEASE_BUILD
pref("network.http.pacing.requests.enabled", false);
pref("network.http.pacing.requests.abtest", false);
#else
pref("network.http.pacing.requests.enabled", true);
pref("network.http.pacing.requests.abtest", true);
#endif
pref("network.http.pacing.requests.min-parallelism", 6);
pref("network.http.pacing.requests.hz", 100);
pref("network.http.pacing.requests.burst", 32);

View File

@ -108,6 +108,7 @@ XPIDL_SOURCES += [
'nsIUploadChannel.idl',
'nsIUploadChannel2.idl',
'nsPISocketTransportService.idl',
'nsPILoadGroupInternal.idl',
]
if CONFIG['MOZ_TOOLKIT_SEARCH']:

View File

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIChannel;
/**
* Dumping ground for load group experimental work.
* This interface will never be frozen. If you are
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(6ef2f8ac-9584-48f3-957a-0c94fff0c8c7)]
interface nsPILoadGroupInternal : nsISupports
{
/**
* Called when the load group has loaded main page and
* subresources. (i.e.essentially DOMComplete)
*
* @param aDefaultChanel
* The request channel for the base apge
*/
void OnEndPageLoad(in nsIChannel aDefaultChannel);
};

View File

@ -21,6 +21,7 @@
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsIHttpChannelInternal.h"
#include "mozilla/Telemetry.h"
using namespace mozilla;
@ -117,6 +118,7 @@ nsLoadGroup::nsLoadGroup(nsISupports* outer)
, mDefaultLoadIsTimed(false)
, mTimedRequests(0)
, mCachedRequests(0)
, mTimedNonCachedRequestsUntilOnEndPageLoad(0)
{
NS_INIT_AGGREGATED(outer);
@ -155,6 +157,7 @@ nsLoadGroup::~nsLoadGroup()
NS_IMPL_AGGREGATED(nsLoadGroup)
NS_INTERFACE_MAP_BEGIN_AGGREGATED(nsLoadGroup)
NS_INTERFACE_MAP_ENTRY(nsILoadGroup)
NS_INTERFACE_MAP_ENTRY(nsPILoadGroupInternal)
NS_INTERFACE_MAP_ENTRY(nsILoadGroupChild)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
@ -631,8 +634,12 @@ nsLoadGroup::RemoveRequest(nsIRequest *request, nsISupports* ctxt,
++mTimedRequests;
TimeStamp timeStamp;
rv = timedChannel->GetCacheReadStart(&timeStamp);
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull())
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
++mCachedRequests;
}
else {
mTimedNonCachedRequestsUntilOnEndPageLoad++;
}
rv = timedChannel->GetAsyncOpen(&timeStamp);
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
@ -812,6 +819,52 @@ nsLoadGroup::GetRootLoadGroup(nsILoadGroup * *aRootLoadGroup)
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsPILoadGroupInternal methods:
NS_IMETHODIMP
nsLoadGroup::OnEndPageLoad(nsIChannel *aDefaultChannel)
{
// On page load of a page with at least 32 resources we want to record
// telemetry on which rate pacing algorithm was used to load the page and
// the page load time.
uint32_t requests = mTimedNonCachedRequestsUntilOnEndPageLoad;
mTimedNonCachedRequestsUntilOnEndPageLoad = 0;
nsCOMPtr<nsITimedChannel> timedChannel =
do_QueryInterface(aDefaultChannel);
if (!timedChannel)
return NS_OK;
nsCOMPtr<nsIHttpChannelInternal> internalHttpChannel =
do_QueryInterface(timedChannel);
if (!internalHttpChannel)
return NS_OK;
TimeStamp cacheReadStart;
TimeStamp asyncOpen;
uint32_t telemetryID;
nsresult rv = timedChannel->GetCacheReadStart(&cacheReadStart);
if (NS_SUCCEEDED(rv))
rv = timedChannel->GetAsyncOpen(&asyncOpen);
if (NS_SUCCEEDED(rv))
rv = internalHttpChannel->GetPacingTelemetryID(&telemetryID);
if (NS_FAILED(rv))
return NS_OK;
// Nothing to do if we don't have a start time or this was
// from the cache or we don't know what profile was used
if (asyncOpen.IsNull() || !cacheReadStart.IsNull() || !telemetryID)
return NS_OK;
if (requests < 32)
return NS_OK;
Telemetry::AccumulateTimeDelta(static_cast<Telemetry::ID>(telemetryID),
asyncOpen);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsISupportsPriority methods:

View File

@ -8,6 +8,7 @@
#include "nsILoadGroup.h"
#include "nsILoadGroupChild.h"
#include "nsPILoadGroupInternal.h"
#include "nsIChannel.h"
#include "nsIStreamListener.h"
#include "nsAgg.h"
@ -26,7 +27,8 @@ class nsILoadGroupConnectionInfo;
class nsLoadGroup : public nsILoadGroup,
public nsILoadGroupChild,
public nsISupportsPriority,
public nsSupportsWeakReference
public nsSupportsWeakReference,
public nsPILoadGroupInternal
{
public:
NS_DECL_AGGREGATED
@ -38,6 +40,7 @@ public:
////////////////////////////////////////////////////////////////////////////
// nsILoadGroup methods:
NS_DECL_NSILOADGROUP
NS_DECL_NSPILOADGROUPINTERNAL
////////////////////////////////////////////////////////////////////////////
// nsILoadGroupChild methods:
@ -87,6 +90,9 @@ protected:
bool mDefaultLoadIsTimed;
uint32_t mTimedRequests;
uint32_t mCachedRequests;
/* For nsPILoadGroupInternal */
uint32_t mTimedNonCachedRequestsUntilOnEndPageLoad;
};
#endif // nsLoadGroup_h__

View File

@ -1399,6 +1399,17 @@ HttpBaseChannel::SetLoadUnblocked(bool aLoadUnblocked)
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetPacingTelemetryID(uint32_t *aID)
{
*aID = 0;
// Don't record pacing algorithm for spdy because it is effectively
// bypassed by the protocol mux
if (!mResponseHead || !mResponseHead->PeekHeader(nsHttp::X_Firefox_Spdy))
*aID = gHttpHandler->PacingTelemetryID();
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority
//-----------------------------------------------------------------------------

View File

@ -149,6 +149,7 @@ public:
NS_IMETHOD SetLoadAsBlocking(bool aLoadAsBlocking);
NS_IMETHOD GetLoadUnblocked(bool *aLoadUnblocked);
NS_IMETHOD SetLoadUnblocked(bool aLoadUnblocked);
NS_IMETHOD GetPacingTelemetryID(uint32_t *aID);
inline void CleanRedirectCacheChainIfNecessary()
{

View File

@ -85,6 +85,7 @@ HTTP_ATOM(Vary, "Vary")
HTTP_ATOM(Version, "Version")
HTTP_ATOM(WWW_Authenticate, "WWW-Authenticate")
HTTP_ATOM(Warning, "Warning")
HTTP_ATOM(X_Firefox_Spdy, "X-Firefox-Spdy")
// methods are atoms too.
//

View File

@ -43,6 +43,7 @@
#include "nsIXULAppInfo.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/Telemetry.h"
#if defined(XP_UNIX)
#include <sys/utsname.h>
@ -189,6 +190,8 @@ nsHttpHandler::nsHttpHandler()
, mConnectTimeout(90000)
, mParallelSpeculativeConnectLimit(6)
, mRequestTokenBucketEnabled(false)
, mRequestTokenBucketABTestEnabled(false)
, mRequestTokenBucketABTestProfile(0)
, mRequestTokenBucketMinParallelism(6)
, mRequestTokenBucketHz(100)
, mRequestTokenBucketBurst(32)
@ -348,8 +351,8 @@ nsHttpHandler::MakeNewRequestTokenBucket()
return;
nsRefPtr<mozilla::net::EventTokenBucket> tokenBucket =
new mozilla::net::EventTokenBucket(mRequestTokenBucketHz,
mRequestTokenBucketBurst);
new mozilla::net::EventTokenBucket(RequestTokenBucketHz(),
RequestTokenBucketBurst());
mConnMgr->UpdateRequestTokenBucket(tokenBucket);
}
@ -774,6 +777,53 @@ nsHttpHandler::MaxSocketCount()
return maxCount;
}
// Different profiles for when the Token Bucket ABTest is enabled
static const uint32_t sNumberTokenBucketProfiles = 7;
static const uint32_t sTokenBucketProfiles[sNumberTokenBucketProfiles][4] = {
// burst, hz, min-parallelism
{ 32, 100, 6, Telemetry::HTTP_PLT_RATE_PACING_0 }, // balanced
{ 16, 100, 6, Telemetry::HTTP_PLT_RATE_PACING_1 }, // start earlier
{ 32, 200, 6, Telemetry::HTTP_PLT_RATE_PACING_2 }, // run faster
{ 32, 50, 6, Telemetry::HTTP_PLT_RATE_PACING_3 }, // run slower
{ 32, 1, 8, Telemetry::HTTP_PLT_RATE_PACING_4 }, // allow only min-parallelism
{ 32, 1, 16, Telemetry::HTTP_PLT_RATE_PACING_5 }, // allow only min-parallelism (larger)
{ 1000, 1000, 1000, Telemetry::HTTP_PLT_RATE_PACING_6 }, // unlimited
};
uint32_t
nsHttpHandler::RequestTokenBucketBurst()
{
return AllowExperiments() && mRequestTokenBucketABTestEnabled ?
sTokenBucketProfiles[mRequestTokenBucketABTestProfile][0] :
mRequestTokenBucketBurst;
}
uint32_t
nsHttpHandler::RequestTokenBucketHz()
{
return AllowExperiments() && mRequestTokenBucketABTestEnabled ?
sTokenBucketProfiles[mRequestTokenBucketABTestProfile][1] :
mRequestTokenBucketHz;
}
uint16_t
nsHttpHandler::RequestTokenBucketMinParallelism()
{
uint32_t rv =
AllowExperiments() && mRequestTokenBucketABTestEnabled ?
sTokenBucketProfiles[mRequestTokenBucketABTestProfile][2] :
mRequestTokenBucketMinParallelism;
return static_cast<uint16_t>(rv);
}
uint32_t
nsHttpHandler::PacingTelemetryID()
{
if (!mRequestTokenBucketEnabled || !mRequestTokenBucketABTestEnabled)
return 0;
return sTokenBucketProfiles[mRequestTokenBucketABTestProfile][3];
}
void
nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
{
@ -1216,40 +1266,6 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
}
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.enabled"))) {
rv = prefs->GetBoolPref(HTTP_PREF("pacing.requests.enabled"),
&cVar);
if (NS_SUCCEEDED(rv))
mRequestTokenBucketEnabled = cVar;
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.min-parallelism"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.min-parallelism"), &val);
if (NS_SUCCEEDED(rv))
mRequestTokenBucketMinParallelism = static_cast<uint16_t>(clamped(val, 1, 1024));
}
bool requestTokenBucketUpdated = false;
if (PREF_CHANGED(HTTP_PREF("pacing.requests.hz"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.hz"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketHz = static_cast<uint32_t>(clamped(val, 1, 10000));
requestTokenBucketUpdated = true;
}
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.burst"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.burst"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketBurst = val ? val : 1;
requestTokenBucketUpdated = true;
}
}
if (requestTokenBucketUpdated) {
mRequestTokenBucket =
new mozilla::net::EventTokenBucket(mRequestTokenBucketHz,
mRequestTokenBucketBurst);
}
//
// Tracking options
//
@ -1269,12 +1285,17 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
}
}
// toggle to true anytime a token bucket related pref is changed.. that
// includes telemetry and allow-experiments because of the abtest profile
bool requestTokenBucketUpdated = false;
//
// Telemetry
//
if (PREF_CHANGED(TELEMETRY_ENABLED)) {
cVar = false;
requestTokenBucketUpdated = true;
rv = prefs->GetBoolPref(TELEMETRY_ENABLED, &cVar);
if (NS_SUCCEEDED(rv)) {
mTelemetryEnabled = cVar;
@ -1284,9 +1305,9 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
//
// network.allow-experiments
//
if (PREF_CHANGED(ALLOW_EXPERIMENTS)) {
cVar = true;
requestTokenBucketUpdated = true;
rv = prefs->GetBoolPref(ALLOW_EXPERIMENTS, &cVar);
if (NS_SUCCEEDED(rv)) {
mAllowExperiments = cVar;
@ -1327,6 +1348,54 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
MakeNewRequestTokenBucket();
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.enabled"))) {
rv = prefs->GetBoolPref(HTTP_PREF("pacing.requests.enabled"),
&cVar);
if (NS_SUCCEEDED(rv)){
requestTokenBucketUpdated = true;
mRequestTokenBucketEnabled = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.abtest"))) {
rv = prefs->GetBoolPref(HTTP_PREF("pacing.requests.abtest"),
&cVar);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketABTestEnabled = cVar;
requestTokenBucketUpdated = true;
if (mRequestTokenBucketABTestEnabled) {
// just taking the remainder is not perfectly uniform but it doesn't
// matter here.
mRequestTokenBucketABTestProfile = rand() % sNumberTokenBucketProfiles;
}
}
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.min-parallelism"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.min-parallelism"), &val);
if (NS_SUCCEEDED(rv))
mRequestTokenBucketMinParallelism = static_cast<uint16_t>(clamped(val, 1, 1024));
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.hz"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.hz"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketHz = static_cast<uint32_t>(clamped(val, 1, 10000));
requestTokenBucketUpdated = true;
}
}
if (PREF_CHANGED(HTTP_PREF("pacing.requests.burst"))) {
rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.burst"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketBurst = val ? val : 1;
requestTokenBucketUpdated = true;
}
}
if (requestTokenBucketUpdated) {
mRequestTokenBucket =
new mozilla::net::EventTokenBucket(RequestTokenBucketHz(),
RequestTokenBucketBurst());
}
#undef PREF_CHANGED
#undef MULTI_PREF_CHANGED
}

View File

@ -108,7 +108,10 @@ public:
bool CritialRequestPrioritization() { return mCritialRequestPrioritization; }
bool UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
uint16_t RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
uint16_t RequestTokenBucketMinParallelism();
uint32_t RequestTokenBucketHz();
uint32_t RequestTokenBucketBurst();
uint32_t PacingTelemetryID();
bool PromptTempRedirect() { return mPromptTempRedirect; }
@ -422,6 +425,8 @@ private:
// For Rate Pacing of HTTP/1 requests through a netwerk/base/src/EventTokenBucket
// Active requests <= *MinParallelism are not subject to the rate pacing
bool mRequestTokenBucketEnabled;
bool mRequestTokenBucketABTestEnabled;
uint32_t mRequestTokenBucketABTestProfile;
uint16_t mRequestTokenBucketMinParallelism;
uint32_t mRequestTokenBucketHz; // EventTokenBucket HZ
uint32_t mRequestTokenBucketBurst; // EventTokenBucket Burst

View File

@ -34,7 +34,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(74d13d41-85cd-490f-9942-300d0c01c726)]
[scriptable, uuid(68074a18-49a8-44d8-852c-320ad4b1d308)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@ -171,4 +171,12 @@ interface nsIHttpChannelInternal : nsISupports
* exclusivity. Default false.
*/
attribute boolean loadUnblocked;
/**
* bug 819734 we implement experimental rate pacing strategies for pages
* with large numbers of subresources. Several experimental profiles are
* available and this attribute corresponds to the profile used for this
* channel. (or at least the one active when it is queried.)
*/
readonly attribute uint32_t pacingTelemetryID;
};

View File

@ -1976,6 +1976,62 @@
"extended_statistics_ok": true,
"description": "Time spent in nsDiskCacheStreamIO::Close() on the main thread (ms)"
},
"HTTP_PLT_RATE_PACING_0": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 0 (burst=32, hz=100, par=6)"
},
"HTTP_PLT_RATE_PACING_1": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 1 (burst=16, hz=100, par=6)"
},
"HTTP_PLT_RATE_PACING_2": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 2 (burst=32, hz=200, par=6)"
},
"HTTP_PLT_RATE_PACING_3": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 3 (burst=32, hz=50, par=6)"
},
"HTTP_PLT_RATE_PACING_4": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 4 (burst=32, hz=1, par=8)"
},
"HTTP_PLT_RATE_PACING_5": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 5 (burst=32, hz=1, par=16)"
},
"HTTP_PLT_RATE_PACING_6": {
"kind": "exponential",
"low": 100,
"high": "30000",
"n_buckets": 100,
"extended_statistics_ok": true,
"description": "HTTP: Total page load time (ms) with rate pacing algorithm 6 (burst=1000, hz=1000, par=1000)"
},
"IDLE_NOTIFY_BACK_MS": {
"kind": "exponential",
"high": "5000",