Closed
Bug 1189829
Opened 10 years ago
Closed 10 years ago
Fix O(n^2) behavior in nsConsoleService::ClearMessagesForWindowID
Categories
(Core :: XPCOM, defect)
Core
XPCOM
Tracking
()
RESOLVED
FIXED
mozilla43
Tracking | Status | |
---|---|---|
firefox41 | --- | unaffected |
firefox42 | + | fixed |
firefox43 | --- | fixed |
People
(Reporter: khuey, Assigned: froydnj)
References
()
Details
(Keywords: regression)
Attachments
(1 file, 2 obsolete files)
10.70 KB,
patch
|
erahm
:
review+
Sylvestre
:
approval-mozilla-aurora+
|
Details | Diff | Splinter Review |
Pretty classic O(n^2) array manipulation behavior there.
Reporter | ||
Comment 1•10 years ago
|
||
[Tracking Requested - why for this release]: We shouldn't ship this.
Comment 2•10 years ago
|
||
The code that khuey's concerned about is (I think):
> 78 for (uint32_t i = 0; i < mBufferSize && mMessages[i]; i++) {
[...]
> 93 uint32_t j = i;
> 94 // Now shift all the following messages
> 95 for (; j < mBufferSize - 1 && mMessages[j + 1]; j++) {
> 96 mMessages[j] = mMessages[j + 1];
> 97 }
[...]
> 105 // Ensure the next iteration handles the messages we just shifted down
> 106 i--;
> 107 }
http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsConsoleService.cpp?mark=78-78,94-97&rev=49ae0961591e#78
This indeed looks like it has O(n^2) performance, and would blow up time-wise if we're clearing a nontrivial portion of mMessages.
![]() |
Assignee | |
Updated•10 years ago
|
Assignee: nobody → nfroyd
Comment 5•10 years ago
|
||
(Side note: should we make mMessages use smart pointers, instead of manually-refcounted raw pointers? e.g. perhaps it should have type "nsCOMArray<nsIConsoleMessage>*" instead of "nsIConsoleMessage**"?
If this seems sane to people who are actually wrestling with this code, I'm happy to file a followup on it, but I'm mentioning it here, because it seems like any patch that tweaks this array [e.g. this bug's patch] will have a nontrivial chance of accidentally breaking the refcounting, as long as we're relying on manual macro invocations. froydnj, what do you think?)
Flags: needinfo?(nfroyd)
Comment 6•10 years ago
|
||
(er, I think I meant nsCOMArray<nsIConsoleMessage> actually -- not a pointer to a nsCOMArray.)
![]() |
Assignee | |
Comment 7•10 years ago
|
||
I am a fan of writing safe code. And yes, doing comment 6 would be preferable, too.
Flags: needinfo?(nfroyd)
Comment 8•10 years ago
|
||
(Cool, thanks. Filed bug 1195486 on converting mMessages to be safer then.)
Comment 9•10 years ago
|
||
Comment on attachment 8648901 [details] [diff] [review]
fix quadratic behavior in nsConsoleService
Review of attachment 8648901 [details] [diff] [review]:
-----------------------------------------------------------------
Good news: Your approach looks good aside from a few corner cases.
Bad news: I think there's enough fundamentally wrong with nsConsoleService's attempt at a circular buffer that we'd be better off just replacing it.
A maximum sized LinkedList feels right here (O(1) insertFront, popLast, remove (when iterating)). We don't need random access, there are only 250 entries max so the overhead of a doubly-linked list is negligible, there is only one console service so no multiplier effect.
This would get rid of a fair amount of "handle the circular buffer thing" logic (which is currently wrong), in general the code would be easier to read, and it would inherently fix the problem we're try to solve here.
All of that said, I can just re-review w/ fixes for the corner cases and we can punt to the follow up.
::: xpcom/base/nsConsoleService.cpp
@@ +80,5 @@
> + for (; i < mBufferSize && mMessages[i]; i++, openSlot++) {
> + // Slide the message down if necessary.
> + if (i != openSlot) {
> + mMessages[openSlot] = mMessages[i];
> + mMessages[i] = nullptr;
Corner cases:
#1 - If we match all entries, mMessages[0] will not be nulled out
#2 - If we *only* match the last entry, it will not be nulled out
@@ +100,3 @@
>
> + // Compensate for incrementing openSlot in the loop header.
> + openSlot--;
Is this always safe if |openSlot == 0|? I'm too lazy to spec spelunk this :)
@@ -105,1 @@
> mFull = false;
Latent bug:
So this is just wrong. We're doing a circular buffer, by setting mFull to false we've effectively corrupted the proper order (element 0 is now the 'oldest' entry, mCurrent is the 'newest' + 1).
Attachment #8648901 -
Flags: review?(erahm)
![]() |
Assignee | |
Comment 10•10 years ago
|
||
(In reply to Eric Rahm [:erahm] from comment #9)
> This would get rid of a fair amount of "handle the circular buffer thing"
> logic (which is currently wrong), in general the code would be easier to
> read, and it would inherently fix the problem we're try to solve here.
Yeah, There are better data structures we could be using here, either rolling our own deque via a linked list or using another one.
> ::: xpcom/base/nsConsoleService.cpp
> @@ +80,5 @@
> > + for (; i < mBufferSize && mMessages[i]; i++, openSlot++) {
> > + // Slide the message down if necessary.
> > + if (i != openSlot) {
> > + mMessages[openSlot] = mMessages[i];
> > + mMessages[i] = nullptr;
>
> Corner cases:
> #1 - If we match all entries, mMessages[0] will not be nulled out
> #2 - If we *only* match the last entry, it will not be nulled out
Does it make you feel any better to know that NS_RELEASE nulls out the pointer it frees? I think that takes care of the corner cases here.
> @@ +100,3 @@
> >
> > + // Compensate for incrementing openSlot in the loop header.
> > + openSlot--;
>
> Is this always safe if |openSlot == 0|? I'm too lazy to spec spelunk this :)
Yes. Arithmetic on unsigned types wraps like you would expect. (Arithmetic that wraps on signed types is undefined behavior.)
> @@ -105,1 @@
> > mFull = false;
>
> Latent bug:
>
> So this is just wrong. We're doing a circular buffer, by setting mFull to
> false we've effectively corrupted the proper order (element 0 is now the
> 'oldest' entry, mCurrent is the 'newest' + 1).
Wow, I just totally botched that review, then, didn't I? Another reason to rewrite this code to make it clearer. I'll fix that.
![]() |
Assignee | |
Comment 11•10 years ago
|
||
Thank you for the thorough review!
This is the same patch, with the mFull bit deleted; I think the NS_RELEASE
nulling behavior takes care of your concerns about corner cases.
Attachment #8648901 -
Attachment is obsolete: true
Attachment #8649339 -
Flags: review?(erahm)
Comment 12•10 years ago
|
||
Comment on attachment 8649339 [details] [diff] [review]
fix quadratic behavior in nsConsoleService
Review of attachment 8649339 [details] [diff] [review]:
-----------------------------------------------------------------
Oh! Good to know about NS_RELEASE nulling, so no concerns there. We still need to properly deal with |mFull| though.
::: xpcom/base/nsConsoleService.cpp
@@ +104,3 @@
>
> + if (openSlot != i) {
> + mCurrent = openSlot;
Unfortunately we need to properly set mFull. It's used in various places to make decisions on how we access the backing array.
Really I think the easiest thing is to just rip out the circular buffer (this isn't hyperbole, the code would become much less tedious and our n^2 issue would be fixed).
Attachment #8649339 -
Flags: review?(erahm)
![]() |
Assignee | |
Comment 13•10 years ago
|
||
All right, the failures on try look to be intermittents and nothing completely
embarrassing, so here's the attempt at rewriting things to be less awful.
Attachment #8649339 -
Attachment is obsolete: true
Attachment #8649445 -
Flags: review?(erahm)
Comment 14•10 years ago
|
||
Comment on attachment 8649445 [details] [diff] [review]
fix quadratic behavior in nsConsoleService
Review of attachment 8649445 [details] [diff] [review]:
-----------------------------------------------------------------
r=me Thank you for taking the time to do the proper fix.
::: xpcom/base/nsConsoleService.cpp
@@ +110,1 @@
> }
Lets set mCurrentSize to zero here.
@@ +441,5 @@
> */
> MutexAutoLock lock(mLock);
>
> + ClearMessages();
> + mCurrentSize = 0;
Can remove this if we move it to ClearMessages.
Attachment #8649445 -
Flags: review?(erahm) → review+
Comment 15•10 years ago
|
||
![]() |
Assignee | |
Comment 16•10 years ago
|
||
I am curious to know whether the additional memory allocations we're doing now have any effect...
Status: NEW → RESOLVED
Closed: 10 years ago
status-firefox43:
--- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla43
![]() |
Assignee | |
Comment 18•10 years ago
|
||
Comment on attachment 8649445 [details] [diff] [review]
fix quadratic behavior in nsConsoleService
Approval Request Comment
[Feature/regressing bug #]: bug 814497
[User impact if declined]: performance badness
[Describe test coverage new/current, TreeHerder]: I think we have tests for this.
[Risks and why]: Low risk.
[String/UUID change made/needed]: None.
Attachment #8649445 -
Flags: approval-mozilla-aurora?
Comment 19•10 years ago
|
||
Comment on attachment 8649445 [details] [diff] [review]
fix quadratic behavior in nsConsoleService
Fix a bad performance regression, taking it.
Attachment #8649445 -
Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Comment 20•10 years ago
|
||
You need to log in
before you can comment on or make changes to this bug.
Description
•