Closed
Bug 1149235
Opened 9 years ago
Closed 9 years ago
Handling of many async scripts in nsScriptLoader::ProcessPendingRequests is slow
Categories
(Core :: DOM: Core & HTML, defect)
Tracking
()
RESOLVED
FIXED
mozilla40
Tracking | Status | |
---|---|---|
firefox40 | --- | fixed |
People
(Reporter: bzbarsky, Assigned: bzbarsky)
References
(Blocks 1 open bug)
Details
Attachments
(3 files, 2 obsolete files)
5.73 KB,
patch
|
smaug
:
review+
|
Details | Diff | Splinter Review |
8.93 KB,
patch
|
smaug
:
review+
|
Details | Diff | Splinter Review |
19.41 KB,
patch
|
smaug
:
review+
|
Details | Diff | Splinter Review |
The current setup in nsScriptLoader::ProcessPendingRequests does this: uint32_t i = 0; while (mEnabled && i < mAsyncRequests.Length()) { if (!mAsyncRequests[i]->mLoading) { request.swap(mAsyncRequests[i]); mAsyncRequests.RemoveElementAt(i); ProcessRequest(request); continue; } ++i; } This is pretty bad when mAsyncRequests.Length() is large: we'll get one callback from necko for each one, and each callback will set mLoading on _one_ thing in the list and then call ProcessPendingRequests. So we get O(N^2) behavior. I'm going to switch to using separate lists for loading and loaded async requests, and doubly linked lists for nsScriptLoadRequest so we don't have to do this silliness and can remove things from the middle of the list quickly. The other option would probably be to use a hashset or something for mLoading/LoadedAsyncRequests, I guess, but I think the general idea of linked lists makes a lot of sense for most of the stuff in scriptloader.
Assignee | ||
Comment 1•9 years ago
|
||
Attachment #8585652 -
Flags: review?(bugs)
Assignee | ||
Updated•9 years ago
|
Assignee: nobody → bzbarsky
Status: NEW → ASSIGNED
Assignee | ||
Comment 2•9 years ago
|
||
Attachment #8585653 -
Flags: review?(bugs)
Assignee | ||
Updated•9 years ago
|
Attachment #8585652 -
Flags: review?(bugs) → review?(jonas)
Assignee | ||
Updated•9 years ago
|
Attachment #8585653 -
Flags: review?(bugs) → review?(jonas)
Assignee | ||
Comment 3•9 years ago
|
||
Attachment #8585654 -
Flags: review?(jonas)
Assignee | ||
Updated•9 years ago
|
Summary: Handling of many async scripts is slow → Handling of many async scripts in nsScriptLoader::ProcessPendingRequests is slow
Assignee | ||
Comment 4•9 years ago
|
||
Attachment #8586464 -
Flags: review?(jonas)
Assignee | ||
Updated•9 years ago
|
Attachment #8585653 -
Attachment is obsolete: true
Attachment #8585653 -
Flags: review?(jonas)
Assignee | ||
Comment 5•9 years ago
|
||
Attachment #8586791 -
Flags: review?(jonas)
Assignee | ||
Updated•9 years ago
|
Attachment #8586464 -
Attachment is obsolete: true
Attachment #8586464 -
Flags: review?(jonas)
My queue is long enough that it'd probably be better to find someone else to review.
Assignee | ||
Updated•9 years ago
|
Attachment #8585652 -
Flags: review?(jonas) → review?(bugs)
Assignee | ||
Updated•9 years ago
|
Attachment #8585654 -
Flags: review?(jonas) → review?(bugs)
Assignee | ||
Updated•9 years ago
|
Attachment #8586791 -
Flags: review?(jonas) → review?(bugs)
Assignee | ||
Comment 7•9 years ago
|
||
Let's try Olli.
Comment 8•9 years ago
|
||
Will do, Tuesday at latest.
Updated•9 years ago
|
Attachment #8585652 -
Flags: review?(bugs) → review+
Comment 9•9 years ago
|
||
Comment on attachment 8586791 [details] [diff] [review] part 2. Switch to using linked lists for nsScriptLoadRequest >- for (uint32_t i = 0; i < mXSLTRequests.Length(); i++) { >- mXSLTRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); >+ for (nsScriptLoadRequest* req = mXSLTRequests.getFirst(); req; >+ req = req->getNext()) { >+ req->FireScriptAvailable(NS_ERROR_ABORT); > } > >- for (uint32_t i = 0; i < mDeferRequests.Length(); i++) { >- mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); >+ for (nsScriptLoadRequest* req = mDeferRequests.getFirst(); req; >+ req = req->getNext()) { >+ req->FireScriptAvailable(NS_ERROR_ABORT); > } > >- for (uint32_t i = 0; i < mAsyncRequests.Length(); i++) { >- mAsyncRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); >+ for (nsScriptLoadRequest* req = mAsyncRequests.getFirst(); req; >+ req = req->getNext()) { >+ req->FireScriptAvailable(NS_ERROR_ABORT); > } > >- for (uint32_t i = 0; i < mNonAsyncExternalScriptInsertedRequests.Length(); i++) { >- mNonAsyncExternalScriptInsertedRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); >+ for(nsScriptLoadRequest* req = mNonAsyncExternalScriptInsertedRequests.getFirst(); >+ req; >+ req = req->getNext()) { >+ req->FireScriptAvailable(NS_ERROR_ABORT); > } Is this setup really safe? I'd do something like for (nsScriptLoadRequest* req = mFoo.getFirst(); req; req = mFoo.getFirst()) I'm worried that req->getNext() uses dead req >+void >+nsScriptLoadRequestList::Clear() >+{ >+ nsScriptLoadRequest* req = getFirst(); >+ while (req) { >+ nsScriptLoadRequest* next = req->getNext(); >+ req->remove(); >+ NS_RELEASE(req); >+ req = next; >+ } >+} this is similar. Just in case Release() has some side effects, would be better to always just take the first item in the list, I think >+ void AppendElement(nsScriptLoadRequest* aElem) { { goes to the next line
Attachment #8586791 -
Flags: review?(bugs) → review-
Comment 10•9 years ago
|
||
er, wait
Comment 11•9 years ago
|
||
(In reply to Olli Pettay [:smaug] from comment #9) > > Is this setup really safe? > I'd do something like > for (nsScriptLoadRequest* req = mFoo.getFirst(); req; req = mFoo.getFirst()) > I'm worried that req->getNext() uses dead req we don't actually remove the items from the list here, and this is happening in dtor, so no one should be able to access to lists here. So fine. > > > >+void > >+nsScriptLoadRequestList::Clear() > >+{ > >+ nsScriptLoadRequest* req = getFirst(); > >+ while (req) { > >+ nsScriptLoadRequest* next = req->getNext(); > >+ req->remove(); > >+ NS_RELEASE(req); > >+ req = next; > >+ } > >+} So this could actually use StealFirst(); nsScriptLoadRequestList::Clear() { while (!IsEmpty()) { nsRefPtr<nsScriptLoadRequest> first = StealFirst(); first->remove(); } }
Comment 12•9 years ago
|
||
Comment on attachment 8586791 [details] [diff] [review] part 2. Switch to using linked lists for nsScriptLoadRequest So with that Clear() simplified and { placement fixed, r+.
Attachment #8586791 -
Flags: review- → review+
Comment 13•9 years ago
|
||
Comment on attachment 8585654 [details] [diff] [review] part 3. Store async requests in the scriptloader in two lists, so we don't have to grovel about looking for loaded ones > if (aElement->GetScriptAsync()) { > request->mIsAsync = true; >- mAsyncRequests.AppendElement(request); > if (!request->mLoading) { >+ mLoadedAsyncRequests.AppendElement(request); > // The script is available already. Run it ASAP when the event > // loop gets a chance to spin. > ProcessPendingRequestsAsync(); >+ } else { >+ mLoadingAsyncRequests.AppendElement(request); > } I guess it might be good to assert in AppendElement that request isn't in any list
Attachment #8585654 -
Flags: review?(bugs) → review+
Assignee | ||
Comment 14•9 years ago
|
||
> So this could actually use StealFirst(); Good catch, though it doesn't need the remove() call: StealFirst did that already. > I guess it might be good to assert in AppendElement that request isn't in any list Will do, though note that the LinkedListNode stuff ends up asserting that anyway.
Assignee | ||
Comment 15•9 years ago
|
||
https://hg.mozilla.org/integration/mozilla-inbound/rev/50cb8f889d23 https://hg.mozilla.org/integration/mozilla-inbound/rev/c15e0ee2ee9f https://hg.mozilla.org/integration/mozilla-inbound/rev/6607b7fca4f4
Comment 16•9 years ago
|
||
https://hg.mozilla.org/mozilla-central/rev/50cb8f889d23 https://hg.mozilla.org/mozilla-central/rev/c15e0ee2ee9f https://hg.mozilla.org/mozilla-central/rev/6607b7fca4f4
Status: ASSIGNED → RESOLVED
Closed: 9 years ago
status-firefox40:
--- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla40
Updated•5 years ago
|
Component: DOM → DOM: Core & HTML
You need to log in
before you can comment on or make changes to this bug.
Description
•