зеркало из https://github.com/mozilla/gecko-dev.git
Bug 539215 - Chunk long tree op queue flushes into multiple runnables in the HTML5 parser. r=bnewman.
--HG-- extra : rebase_source : 31f125d0e833aaea2c1bd4755c16d5e7fd5f2e83
This commit is contained in:
Родитель
76f524a22a
Коммит
cf81aa1f24
|
@ -2827,7 +2827,7 @@ pref("geo.enabled", true);
|
|||
|
||||
// Enable/Disable HTML5 parser
|
||||
pref("html5.enable", false);
|
||||
// Toggle which thread the HTML5 parser uses for streama parsing
|
||||
// Toggle which thread the HTML5 parser uses for stream parsing
|
||||
pref("html5.offmainthread", true);
|
||||
// Time in milliseconds between the start of the network stream and the
|
||||
// first time the flush timer fires in the off-the-main-thread HTML5 parser.
|
||||
|
@ -2838,6 +2838,14 @@ pref("html5.flushtimer.continuedelay", 150);
|
|||
// Time in milliseconds between timer firings once the timer has starting
|
||||
// firing.
|
||||
pref("html5.flushtimer.interval", 100);
|
||||
// Initial max length for number of tree ops in on flush.
|
||||
pref("html5.opqueue.initiallengthlimit", 200);
|
||||
// Maximum time in milliseconds to spend flushing the tree op queue when not forced to completion
|
||||
pref("html5.opqueue.maxtime", 100);
|
||||
// Minimun number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
|
||||
pref("html5.opqueue.minlength", 100);
|
||||
// Maximum number of tree ops to flush regardless of time (takes precedence over the maxtime pref)
|
||||
pref("html5.opqueue.maxlength", 4500); // most top sites stay under this value
|
||||
|
||||
// Push/Pop/Replace State prefs
|
||||
pref("browser.history.allowPushState", true);
|
||||
|
|
|
@ -72,6 +72,7 @@ nsHtml5Module::InitializeStatics()
|
|||
nsHtml5TreeBuilder::initializeStatics();
|
||||
nsHtml5UTF16Buffer::initializeStatics();
|
||||
nsHtml5StreamParser::InitializeStatics();
|
||||
nsHtml5TreeOpExecutor::InitializeStatics();
|
||||
#ifdef DEBUG
|
||||
sNsHtml5ModuleInitialized = PR_TRUE;
|
||||
#endif
|
||||
|
|
|
@ -381,7 +381,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
if (mTreeBuilder->HasScript()) {
|
||||
// No need to flush characters, because an end tag was tokenized last
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(); // run the ops
|
||||
mExecutor->Flush(PR_TRUE); // run the ops
|
||||
}
|
||||
if (mBlocked) {
|
||||
// mExecutor->WillInterrupt();
|
||||
|
@ -397,7 +397,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
// Scripting semantics require a forced tree builder flush here
|
||||
mTreeBuilder->flushCharacters(); // Flush trailing characters
|
||||
mTreeBuilder->Flush(); // Move ops to the executor
|
||||
mExecutor->Flush(); // run the ops
|
||||
mExecutor->Flush(PR_TRUE); // run the ops
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -492,7 +492,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
|
|||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
mTokenizer->end();
|
||||
mExecutor->DropParserAndPerfHint();
|
||||
mAtomTable.Clear();
|
||||
|
@ -600,7 +600,7 @@ nsHtml5Parser::ParseUntilBlocked()
|
|||
mTokenizer->eof();
|
||||
mTreeBuilder->StreamEnded();
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
mTokenizer->end();
|
||||
return;
|
||||
} else {
|
||||
|
@ -645,7 +645,7 @@ nsHtml5Parser::ParseUntilBlocked()
|
|||
}
|
||||
if (mTreeBuilder->HasScript()) {
|
||||
mTreeBuilder->Flush();
|
||||
mExecutor->Flush();
|
||||
mExecutor->Flush(PR_TRUE);
|
||||
}
|
||||
if (mBlocked) {
|
||||
// mExecutor->WillInterrupt();
|
||||
|
|
|
@ -133,7 +133,7 @@ class nsHtml5ExecutorFlusher : public nsRunnable
|
|||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mExecutor->Flush();
|
||||
mExecutor->Flush(PR_FALSE);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -57,10 +57,6 @@
|
|||
#include "nsHtml5TreeBuilder.h"
|
||||
#include "nsHtml5StreamParser.h"
|
||||
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_MAX_QUEUE_TIME 3000UL // milliseconds
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_DEFAULT_QUEUE_LENGTH 200
|
||||
#define NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH 100
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5TreeOpExecutor)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHtml5TreeOpExecutor)
|
||||
|
@ -80,6 +76,41 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHtml5TreeOpExecutor, nsContent
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mOwnedElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueLengthLimit = 200;
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxTime = 100; // milliseconds
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMinLength = 100;
|
||||
PRInt32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxLength = 4500;
|
||||
|
||||
// static
|
||||
void
|
||||
nsHtml5TreeOpExecutor::InitializeStatics()
|
||||
{
|
||||
// Changes to the initial max length pref are intentionally allowed
|
||||
// to reset the run-time-calibrated value.
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.initiallengthlimit",
|
||||
&sTreeOpQueueLengthLimit);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxtime",
|
||||
&sTreeOpQueueMaxTime);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.minlength",
|
||||
&sTreeOpQueueMinLength);
|
||||
nsContentUtils::AddIntPrefVarCache("html5.opqueue.maxlength",
|
||||
&sTreeOpQueueMaxLength);
|
||||
// Now do some sanity checking to prevent breaking the app via
|
||||
// about:config so badly that it can't be recovered via about:config.
|
||||
if (sTreeOpQueueMinLength <= 0) {
|
||||
sTreeOpQueueMinLength = 200;
|
||||
}
|
||||
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
|
||||
}
|
||||
if (sTreeOpQueueMaxLength < sTreeOpQueueMinLength) {
|
||||
sTreeOpQueueMaxLength = sTreeOpQueueMinLength;
|
||||
}
|
||||
if (sTreeOpQueueMaxTime <= 0) {
|
||||
sTreeOpQueueMaxTime = 200;
|
||||
}
|
||||
}
|
||||
|
||||
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
|
||||
{
|
||||
// zeroing operator new for everything
|
||||
|
@ -135,8 +166,7 @@ nsHtml5TreeOpExecutor::DidBuildModel(PRBool aTerminated)
|
|||
printf("TOKENIZER-SAFE SCRIPTS: %d\n", sTokenSafeDocWrites);
|
||||
printf("TREEBUILDER-SAFE SCRIPTS: %d\n", sTreeSafeDocWrites);
|
||||
#endif
|
||||
#ifdef DEBUG_hsivonen
|
||||
printf("MAX INSERTION BATCH LEN: %d\n", sInsertionBatchMaxLength);
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("MAX NOTIFICATION BATCH LEN: %d\n", sAppendBatchMaxSize);
|
||||
if (sAppendBatchExaminations != 0) {
|
||||
printf("AVERAGE SLOTS EXAMINED: %d\n", sAppendBatchSlotsExamined / sAppendBatchExaminations);
|
||||
|
@ -269,8 +299,23 @@ nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
|
|||
BeginDocUpdate();
|
||||
}
|
||||
|
||||
class nsHtml5ExecutorReflusher : public nsRunnable
|
||||
{
|
||||
private:
|
||||
nsRefPtr<nsHtml5TreeOpExecutor> mExecutor;
|
||||
public:
|
||||
nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mExecutor->Flush(PR_FALSE);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
nsHtml5TreeOpExecutor::Flush()
|
||||
nsHtml5TreeOpExecutor::Flush(PRBool aForceWholeQueue)
|
||||
{
|
||||
if (!mParser) {
|
||||
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
||||
|
@ -294,14 +339,35 @@ nsHtml5TreeOpExecutor::Flush()
|
|||
BeginDocUpdate();
|
||||
|
||||
PRIntervalTime flushStart = 0;
|
||||
PRUint32 opQueueLength = mOpQueue.Length();
|
||||
if (opQueueLength > NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) { // avoid computing averages with too few ops
|
||||
flushStart = PR_IntervalNow();
|
||||
PRUint32 numberOfOpsToFlush = mOpQueue.Length();
|
||||
PRBool reflushNeeded = PR_FALSE;
|
||||
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
if (numberOfOpsToFlush > sOpQueueMaxLength) {
|
||||
sOpQueueMaxLength = numberOfOpsToFlush;
|
||||
}
|
||||
mElementsSeenInThisAppendBatch.SetCapacity(opQueueLength * 2);
|
||||
// XXX alloc failure
|
||||
printf("QUEUE LENGTH: %d\n", numberOfOpsToFlush);
|
||||
printf("MAX QUEUE LENGTH: %d\n", sOpQueueMaxLength);
|
||||
#endif
|
||||
|
||||
if (aForceWholeQueue) {
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
|
||||
flushStart = PR_IntervalNow(); // compute averages only if enough ops
|
||||
}
|
||||
} else {
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueMinLength) {
|
||||
flushStart = PR_IntervalNow(); // compute averages only if enough ops
|
||||
if (numberOfOpsToFlush > (PRUint32)sTreeOpQueueLengthLimit) {
|
||||
numberOfOpsToFlush = (PRUint32)sTreeOpQueueLengthLimit;
|
||||
reflushNeeded = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mElementsSeenInThisAppendBatch.SetCapacity(numberOfOpsToFlush * 2);
|
||||
|
||||
const nsHtml5TreeOperation* start = mOpQueue.Elements();
|
||||
const nsHtml5TreeOperation* end = start + opQueueLength;
|
||||
const nsHtml5TreeOperation* end = start + numberOfOpsToFlush;
|
||||
for (nsHtml5TreeOperation* iter = (nsHtml5TreeOperation*)start; iter < end; ++iter) {
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
// The previous tree op caused a call to nsIParser::Terminate();
|
||||
|
@ -311,22 +377,29 @@ nsHtml5TreeOpExecutor::Flush()
|
|||
iter->Perform(this, &scriptElement);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_hsivonen
|
||||
if (mOpQueue.Length() > sInsertionBatchMaxLength) {
|
||||
sInsertionBatchMaxLength = opQueueLength;
|
||||
if (NS_LIKELY(mParser)) {
|
||||
mOpQueue.RemoveElementsAt(0, numberOfOpsToFlush);
|
||||
} else {
|
||||
mOpQueue.Clear(); // only for diagnostics in the destructor of this class
|
||||
}
|
||||
#endif
|
||||
mOpQueue.Clear();
|
||||
|
||||
if (flushStart) {
|
||||
PRUint32 delta = PR_IntervalToMilliseconds(PR_IntervalNow() - flushStart);
|
||||
sTreeOpQueueMaxLength = delta ?
|
||||
(PRUint32)((NS_HTML5_TREE_OP_EXECUTOR_MAX_QUEUE_TIME * (PRUint64)opQueueLength) / delta) :
|
||||
0;
|
||||
if (sTreeOpQueueMaxLength < NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH) {
|
||||
sTreeOpQueueMaxLength = NS_HTML5_TREE_OP_EXECUTOR_MIN_QUEUE_LENGTH;
|
||||
sTreeOpQueueLengthLimit = delta ?
|
||||
(PRUint32)(((PRUint64)sTreeOpQueueMaxTime * (PRUint64)numberOfOpsToFlush)
|
||||
/ delta) :
|
||||
sTreeOpQueueMaxLength; // if the delta is less than one ms, use max
|
||||
if (sTreeOpQueueLengthLimit < sTreeOpQueueMinLength) {
|
||||
// both are signed and sTreeOpQueueMinLength is always positive, so this
|
||||
// also takes care of the theoretical overflow of sTreeOpQueueLengthLimit
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMinLength;
|
||||
}
|
||||
#ifdef DEBUG_hsivonen
|
||||
printf("QUEUE MAX LENGTH: %d\n", sTreeOpQueueMaxLength);
|
||||
if (sTreeOpQueueLengthLimit > sTreeOpQueueMaxLength) {
|
||||
sTreeOpQueueLengthLimit = sTreeOpQueueMaxLength;
|
||||
}
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("FLUSH DURATION (millis): %d\n", delta);
|
||||
printf("QUEUE NEW MAX LENGTH: %d\n", sTreeOpQueueLengthLimit);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -334,12 +407,21 @@ nsHtml5TreeOpExecutor::Flush()
|
|||
|
||||
mFlushState = eNotFlushing;
|
||||
|
||||
if (!mParser) {
|
||||
if (NS_UNLIKELY(!mParser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scriptElement) {
|
||||
NS_ASSERTION(!reflushNeeded, "Got scriptElement when queue not fully flushed.");
|
||||
RunScript(scriptElement); // must be tail call when mFlushState is eNotFlushing
|
||||
} else if (reflushNeeded) {
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
printf("REFLUSH SCHEDULED.\n");
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(flusher))) {
|
||||
NS_WARNING("failed to dispatch executor flush event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,9 +661,8 @@ nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* a
|
|||
static_cast<nsHtml5Parser*> (mParser.get())->InitializeDocWriteParserState(aState, aLine);
|
||||
}
|
||||
|
||||
PRUint32 nsHtml5TreeOpExecutor::sTreeOpQueueMaxLength = NS_HTML5_TREE_OP_EXECUTOR_DEFAULT_QUEUE_LENGTH;
|
||||
#ifdef DEBUG_hsivonen
|
||||
PRUint32 nsHtml5TreeOpExecutor::sInsertionBatchMaxLength = 0;
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
PRUint32 nsHtml5TreeOpExecutor::sOpQueueMaxLength = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchMaxSize = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined = 0;
|
||||
PRUint32 nsHtml5TreeOpExecutor::sAppendBatchExaminations = 0;
|
||||
|
|
|
@ -78,14 +78,19 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
|
||||
|
||||
static void InitializeStatics();
|
||||
|
||||
private:
|
||||
#ifdef DEBUG_hsivonen
|
||||
static PRUint32 sInsertionBatchMaxLength;
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
static PRUint32 sOpQueueMaxLength;
|
||||
static PRUint32 sAppendBatchMaxSize;
|
||||
static PRUint32 sAppendBatchSlotsExamined;
|
||||
static PRUint32 sAppendBatchExaminations;
|
||||
#endif
|
||||
static PRUint32 sTreeOpQueueMaxLength;
|
||||
static PRInt32 sTreeOpQueueLengthLimit;
|
||||
static PRInt32 sTreeOpQueueMaxTime;
|
||||
static PRInt32 sTreeOpQueueMinLength;
|
||||
static PRInt32 sTreeOpQueueMaxLength;
|
||||
|
||||
/**
|
||||
* Whether EOF needs to be suppressed
|
||||
|
@ -241,7 +246,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
const nsIContentPtr* first = mElementsSeenInThisAppendBatch.Elements();
|
||||
const nsIContentPtr* last = first + mElementsSeenInThisAppendBatch.Length() - 1;
|
||||
for (const nsIContentPtr* iter = last; iter >= first; --iter) {
|
||||
#ifdef DEBUG_hsivonen
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
sAppendBatchSlotsExamined++;
|
||||
#endif
|
||||
if (*iter == aParent) {
|
||||
|
@ -256,7 +261,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
if (newParent) {
|
||||
mPendingNotifications.AppendElement(aParent);
|
||||
}
|
||||
#ifdef DEBUG_hsivonen
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
sAppendBatchExaminations++;
|
||||
#endif
|
||||
}
|
||||
|
@ -270,7 +275,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
iter->Fire();
|
||||
}
|
||||
mPendingNotifications.Clear();
|
||||
#ifdef DEBUG_hsivonen
|
||||
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
|
||||
if (mElementsSeenInThisAppendBatch.Length() > sAppendBatchMaxSize) {
|
||||
sAppendBatchMaxSize = mElementsSeenInThisAppendBatch.Length();
|
||||
}
|
||||
|
@ -311,7 +316,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
|
||||
nsISupports* aContainer, nsIChannel* aChannel);
|
||||
|
||||
void Flush();
|
||||
void Flush(PRBool aForceWholeQueue);
|
||||
|
||||
void MaybeSuspend();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче