Closed
Bug 407442
Opened 17 years ago
Closed 17 years ago
nsEventListenerManager is allocation happy
Categories
(Core :: DOM: UI Events & Focus Handling, defect, P1)
Tracking
()
RESOLVED
FIXED
mozilla1.9beta4
People
(Reporter: pavlov, Assigned: dwitte)
References
Details
(Keywords: memory-footprint, perf)
Attachments
(2 files, 7 obsolete files)
|
59.43 KB,
patch
|
sicking
:
review+
sicking
:
superreview+
beltzner
:
approval1.9+
|
Details | Diff | Splinter Review |
|
11.07 KB,
patch
|
sicking
:
review+
sicking
:
superreview+
|
Details | Diff | Splinter Review |
Two parts to this, but they both revolve around mListeners.
mListeners holds nsListenerStruct*'s -- this seems pretty dumb, it should hold nsListenerStruct's instead -- we're resizing the array, might as well resize it to hold the elements as well.
the second part, which I haven't got any real numbers on aside to say that I don't see any VoidArray allocations bigger than 64 bytes happening on mListeners, is that mListeners should probably be an nsAutoTArray<nsListenerStruct, 16> (maybe smaller, how many nsEventListenerManagers do we have -- if just one, 16 is probably fine -- we can always adjust it later)
| Reporter | ||
Updated•17 years ago
|
Comment 1•17 years ago
|
||
Every DOMNode (+ window) may have an ELM. I *guess* the average listener count is
closer to 1 (maybe 1-3), or in case of |window| object perhaps something between
5 and 10.
Yup, i don't think we want to use an nsAutoTArray here unfortunately. Or possibly use one with only 1 slot in it. The downside with having 1 slot would of course be that it'd be wasted every time there is more than one listener.
We definitely should store nsListenerStructs though. And eventually stick all this stuff into the same arena as the nodes, once we have that up and running.
| Assignee | ||
Comment 3•17 years ago
|
||
i started the browser, visited about 8 sites on trunk, and closed down. in
total, there were 1898 ELM's constructed. i looked at the maximum size of
mListeners; if we use an nsAutoTArray<, 4> we'll avoid alloc in 91% of cases;
<, 8> would get us to 97%. nsListenerStructs aren't small though (24 bytes on
32-bit), so assuming all 1898 are live at a given time (e.g. if all 8 sites
were open in tabs), which i didn't confirm, then we'll be wasting at
most ~80kb with a <, 4>. too much?
sicking, what kind of arena are you thinking of? i'm curious if/how you'll be handling deallocations.
Did you by any chance get a full histogram of the distribution? Would be great if you could post or attach it here if so. Also, were you checking the highest number of listeners an ELM had, or the number of listeners when it was killed? It probably won't differ much in the common case, but it's the former that is most interesting.
My guess is that using <, 1> is a good number. Did you see how many ELMs had exactly 1 listener?
Oh, and the arena work is going on in bug 403830. It'll use the existing PLArena for now, but we'll probably write a new one once bug 403830 is landed.
| Assignee | ||
Comment 6•17 years ago
|
||
i measured the maximum size of mListeners; many listeners did indeed have elements removed at some point (or the list cleared; in fact there were 5181 calls to mListeners.Clear() - a pretty well-hit path!). the histogram of maximum sizes is:
1: 958
2: 595
3: 110
4: 87
5: 22
6: 12
7: 7
8: 1
9: 23
10: 10
11: 3
12: 2
13: 1
14: 0
15: 1
16: 1
17: 3
18+: 0
| Assignee | ||
Comment 7•17 years ago
|
||
(In reply to comment #5)
> Oh, and the arena work is going on in bug 403830. It'll use the existing
> PLArena for now, but we'll probably write a new one once bug 403830 is landed.
i've run into the "need-an-easy-way-to-mark-and-deallocate-individual-arena-pages" problem before, so if we can make PLArenas more awesome and get some standardized support in for something like that, i'd love to help out. ;)
| Assignee | ||
Comment 8•17 years ago
|
||
pretty simple, the only slightly yuck part was in nsEventListenerManager::HandleEvent() - we save a copy of the entire mListeners array, then enter a loop that "does stuff". i'm not sure exactly what, because i'm no content expert, but i'm guessing the loop can modify the mListeners array itself (i put an XXX comment there). the old code checked to see if elements are removed by doing a pointer comparison between elements of the original and current mListener arrays, but now we've gotta do a full-on equality check between the nsListerStruct entries themselves. :(
this whole thing is O(n^2) anyway, and there are better ways to do it, but i'm gonna defer to the experts. sicking, jst, do you have any ideas? (at the very least, maybe the equality check doesn't need to compare all the elements of the struct...)
| Assignee | ||
Updated•17 years ago
|
Attachment #292567 -
Flags: review? → review?(jonas)
+ nsTArray<nsListenerStruct> originalListeners(mListeners);
Why isn't this an nsAutoTArray?
Comment 10•17 years ago
|
||
(In reply to comment #8)
> but i'm guessing the loop can modify the mListeners
> array itself (i put an XXX comment there).
If not, we might as well use a stack-based nsAutoTArray (with N=16?) there.
| Assignee | ||
Comment 11•17 years ago
|
||
(In reply to comment #9)
> Why isn't this an nsAutoTArray?
i ran into compilation errors making it an nsAutoTArray - turns out there's no copy constructor defined:
http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsTArray.h#716
so we either need to add one or eat the malloc. (there'll only be one, at least, since the array can size appropriately on construction...)
Comment 12•17 years ago
|
||
Note, nsEventListenerManager::HandleEvent is pretty hot method.
How slow or fast is nsTArray<nsListenerStruct> originalListeners(mListeners)
comparing to
PRInt32 count = mListeners.Count();
nsVoidArray originalListeners(count);
originalListeners = mListeners;
The answer to your XXX question is yes.
That loop is calling event listeners which may execute anything (C++ or JS ...)
and call AddEventListener or RemoveEventListener.
== operator must check all the other members except
mHandlerIsString and mTypeData.
Why are you removing mListenersRemoved? It is actually needed, because
mListenerRemoved isn't set to PR_TRUE when disconnecting from mTarget.
| Assignee | ||
Comment 13•17 years ago
|
||
(In reply to comment #12)
> Note, nsEventListenerManager::HandleEvent is pretty hot method.
ok. maybe we want to add a copy constructor to nsAutoTArray then? :/
> How slow or fast is nsTArray<nsListenerStruct> originalListeners(mListeners)
if you mean compared to the old code, pretty much the same - both do one malloc, although with patch we'll be calling the copy constructors of the nsListenerStructs, which will have overhead since it's more data...
> == operator must check all the other members except
> mHandlerIsString and mTypeData.
thanks, i'll make that change.
> Why are you removing mListenersRemoved? It is actually needed, because
> mListenerRemoved isn't set to PR_TRUE when disconnecting from mTarget.
you're right, i misread what the loop was doing with it. i'll add it back.
Comment 14•17 years ago
|
||
(In reply to comment #13)
> (In reply to comment #12)
> > Note, nsEventListenerManager::HandleEvent is pretty hot method.
>
> ok. maybe we want to add a copy constructor to nsAutoTArray then? :/
No, just use AppendElements(const nsTArray<Item>& array), it'll only allocate if you need more than N elements for nsAutoTArray<..., N>
| Assignee | ||
Updated•17 years ago
|
Attachment #292567 -
Flags: review?(jonas)
| Assignee | ||
Comment 15•17 years ago
|
||
add back mListenersRemoved; shorten operator==; use AppendElements() (duh!)
Attachment #292567 -
Attachment is obsolete: true
Comment 16•17 years ago
|
||
Is 32 too much? Does the value affect to the event dispatch 'depth' test
http://mozilla.pettay.fi/moztests/events/event_loop.html
(Currently trunk build on x86 linux gives result 150, 1.8.1 build only 12.
IIRC x86_64 trunk builds are > 90 too)
| Assignee | ||
Comment 17•17 years ago
|
||
(In reply to comment #16)
> Is 32 too much? Does the value affect to the event dispatch 'depth' test
the actual value is transparent to consumers; it only affects how much stack storage the array includes internally. i chose 32 to catch the largest array size in my experiment (comment 6) with some healthy headroom. 32 * 24 = 768, which should be fine, though i could drop this down to 24...
(also, i get 98 for your test on my trunk x86_64 linux build.)
Comment 18•17 years ago
|
||
(In reply to comment #17)
> the actual value is transparent to consumers; it only affects how much stack
> storage the array includes internally.
That is exactly the problem why branch can't handle deep event dispatch loops.
Not that I've seen any bugs filed on that, but when redesigning event
dispatching, I kept that in mind.
| Assignee | ||
Comment 19•17 years ago
|
||
w/ patch @ <, 32> i get 79, @ <, 24> i get 82. is this an acceptable tradeoff, given that we're avoiding a malloc?
| Assignee | ||
Comment 20•17 years ago
|
||
also @ <, 8> i get 92. so since keeping this small matters, maybe we should use that given it catches 98% of cases.
Comment 21•17 years ago
|
||
Would it make sense to use the same value as in mListeners?
Even 2 should catch >90%. You could #define the size in .h.
And btw, please align nsEventListenerManager member variables properly.
Given that we, in a pretty hot codepath, copy all listeners into another array, I'm not sure that this approach is a good one as we'd both end up copying much more data, and do some extra refcounting.
Either we should focus on moving the nsListenerStructs into an arena, or we should first eliminate the copying.
We could use a solution similar to nsTObserverArray, but we'd need to make sure that newly added observers won't get notified. This could actually be done and still reuse the existing nsTObserverArray I think.
| Assignee | ||
Comment 23•17 years ago
|
||
(In reply to comment #22)
> We could use a solution similar to nsTObserverArray
excellent! i'll look into it. this is exactly what i was wishing we had... ;)
I'm actually already working on such a patch. Though if you want to take over that work I wouldn't mind attaching what I have so far :)
The other question is, if we can move the nsListenerStructs into the array itself, do we really want to? The thing is that if we move them into the array we can't allocate them from an arena since nsTArray can't do that.
Though, come to think about it, it's possible that we could tweak nsTArray to support using arenas for allocations, but I'll have to look into that in a separate bug.
| Assignee | ||
Comment 25•17 years ago
|
||
yeah, anything we do with nsTArray or nsTObserverArray is gonna be orthogonal to arenas. it'd be neat to add support for custom allocators into our arrays. just tack on an extra template argument for a class that provides allocation and deallocation methods, and provide default allocators for people who don't care?
we might also switch nsTObserverArray over to nsTArray, and provide an nsAutoTObserverArray or somesuch... what do you think?
| Assignee | ||
Comment 26•17 years ago
|
||
Comment on attachment 292574 [details] [diff] [review]
v2
obsolete per nsTObserverArray.
Attachment #292574 -
Attachment is obsolete: true
| Assignee | ||
Comment 27•17 years ago
|
||
(by my last sentence in comment 25, i meant switch the internal implementation of nsTObserverArray.)
Unfortunately it's not as simple as tacking on another template argument. For one allocations currently happen in a non-templated function. Also, for arenas you'd also need an extra member holding a pointer to which arena is being used. But you wouldn't want to pay the price for that extra member in arrays that are not using an arena.
It's doable, but not simple. Patches accepted, though I have some ideas in mind that I might write up if it turns out that we can get big wins.
| Assignee | ||
Comment 29•17 years ago
|
||
i have a patch that makes nsTObserverArray inherit (protectedly) from nsTArray instead of nsVoidArray, and provides an nsAutoTObserverArray. this would let us implement the original suggestion of storing nsListenerStructs directly. if you want, i can file another bug to get that part in, and then the patch here will be trivial. ;)
Please attach it all here.
| Assignee | ||
Comment 31•17 years ago
|
||
as advertised. (there's some extra newlines in there so diff would produce something more readable; please ignore them.)
this makes nsTObserverArray inherit from nsTArray, and does some API tweaking/additions to make things more or less consistent with nsTArray. (most of the inline API methods are copy/pasted from nsTArray, so nothing special's going on here.) i only implemented a subset of what nsTArray provides, since we don't want to get too fancy with removing/inserting ranges of elements (updating iterators would get tricky). so we still only support prepending and appending. i also expanded out the iterator GetNext() methods into IsMore()/GetNext(), since we don't know if the template type is a pointer or a concrete class and hence can't return null through GetNext().
for nsAutoTArray to work, it turns out nsTArray_base assumes the auto buffer is stored immediately after nsTArray_base::mHdr - so nsTObserverArray can't have any member variables (they'll get put in between). that's why there's a separate IteratorList container class which nsTObserverArray inherits from first. not that elegant, but it works :/
and of course, this also rolls in the ELM change with a <, 2> autoarray.
| Assignee | ||
Comment 32•17 years ago
|
||
with this patch, on 64bit, i get 101 on smaug's eventloop test.
| Assignee | ||
Comment 33•17 years ago
|
||
oh; i also added a Contains() method to nsTArray, since it seemed like a more readable form for the common use case IndexOf() != -1.
I'm not a fan of the nsTObserverArray changes here. First of all, moving code from the .cpp file to inline in the .h file is going to increase binary size a lot.
Not sure why you want to make nsTObserverArray inherit nsTArray, that seems to only give you hassle.
A better implementation would IMHO to make nsTObserverArray contain an nsTArray. If you make it the second member you'll avoid all the hassle you currently have with having to use two base classes.
Also, why is nsTObserverArray implementing so many functions? It doesn't need to be a general-purpose array class, so I'd rather just keep a minimal amount of code.
| Assignee | ||
Comment 35•17 years ago
|
||
(In reply to comment #34)
> I'm not a fan of the nsTObserverArray changes here. First of all, moving code
> from the .cpp file to inline in the .h file is going to increase binary size a
> lot.
agreed, it'd be nice to avoid that. i'll factor the two methods in question back out into the .cpp as much as i can.
> Not sure why you want to make nsTObserverArray inherit nsTArray, that seems to
> only give you hassle.
it's necessary for nsAutoTObserverArray... that needs access to the protected Header member of nsTArray_base, which is only possible through inheritance. i wish we could just have nsTArray as a member, but i don't see a way... anyway, it's not that big a deal, it's just the one base class (which we'll have anyway, per your above suggestion) :/
> Also, why is nsTObserverArray implementing so many functions? It doesn't need
> to be a general-purpose array class, so I'd rather just keep a minimal amount
> of code.
fair enough - i just wanted to keep the interfaces as consistent as possible, since there's no codesize cost to doing so. i'll trim it down.
| Assignee | ||
Comment 36•17 years ago
|
||
talked with sicking, and changed things around a bit:
- provides a specialization nsAutoTArray<T, 0> that just inherits from nsTArray
- makes ns{Auto}TObserverArray use nsAutoTArray<T, N> as a member, relying on the goodness of aforementioned specialization to do the "right thing" for plain ol' nsTObserverArray
this is a less hackish solution overall, and avoids the inheritance mess with nsTArray.
he also asked me to store the nsListenerStructs as ordinary pointers again rather than complete classes, though personally i'd rather see the latter :)
Attachment #293471 -
Attachment is obsolete: true
| Assignee | ||
Comment 37•17 years ago
|
||
... and this one actually works :)
Attachment #293605 -
Attachment is obsolete: true
Comment on attachment 293606 [details] [diff] [review]
round 2
>Index: xpcom/glue/nsTArray.h
>+// specialization for N = 0. this makes the inheritance model easier for
>+// classes extending nsTArray.
Hmm.. this isn't entirely true, is it? I'd say that it makes it easier for templated users of nsAutoTArray.
>Index: xpcom/glue/nsTObserverArray.h
>+ // This method provides direct access to the i'th element of the array.
>+ // The given index must be within the array bounds.
>+ // @param i The index of an element in the array.
>+ // @return A reference to the i'th element of the array.
>+ elem_type& ObserverAt(index_type i) {
>+ return mArray.ElementAt(i);
>+ }
>+
>+ // Same as above, but readonly.
>+ const elem_type& ObserverAt(index_type i) const {
>+ return mArray.ElementAt(i);
>+ }
Feel free to rename these to ElementAt etc. Feel free not to though if it's too much work.
>+ PRBool Contains(const elem_type& elem) const {
>+ return IndexOf(elem) != base_type::NoIndex;
>+ }
Do what nsTArray does here and templetize the elem_type argument.
>+ index_type IndexOf(const elem_type& elem, index_type start = 0) const {
>+ return mArray.IndexOf(elem, start);
>+ }
Same here.
>+ index_type LastIndexOf(const elem_type& elem,
>+ index_type start = base_type::NoIndex) const {
>+ return mArray.LastIndexOf(elem, start);
>+ }
I'd leave this one out entirely.
>+ elem_type* PrependObserver(const elem_type& elem) {
>+ elem_type* result = mArray.InsertElementAt(0, elem);
>+ if (result)
>+ AdjustIterators(0, 1);
>+ return result;
> }
Kill this
>+ elem_type* PrependObserver() {
>+ elem_type* result = mArray.InsertElementAt(0);
>+ if (result)
>+ AdjustIterators(0, 1);
>+ return result;
>+ }
And this. I'm not a big fan of the PrependObserver function, so lets just keep the minimum number of them.
>+ elem_type* AppendObserver(const elem_type& elem) {
>+ return mArray.AppendElement(elem);
> }
Templetize here too.
>+ PRBool AppendObserverUnlessExists(const elem_type& elem) {
>+ return Contains(elem) || AppendObserver(elem) != nsnull;
> }
And here.
>+ PRBool operator <(const ForwardIterator& aOther) const {
>+ return iter_type::mPosition < aOther.mPosition;
>+ }
Assert that they're both iterating the same array.
>+ PRBool IsMore() const {
>+ return iter_type::mPosition < iter_type::mArray.Length();
> }
Rename to HasMore()
>+ PRBool IsMore() const {
>+ return *this < mEnd;
>+ }
Same here.
> // XXXbz I wish I didn't have to pass in the observer type, but I
> // don't see a way to get it out of array_.
Add that this only works if the elements are pointers to XPCOM objects.
>Index: content/events/src/nsEventListenerManager.cpp
> nsEventListenerManager::RemoveAllListeners()
> {
>- PRInt32 count = mListeners.Count();
>+ PRInt32 count = mListeners.Length();
> for (PRInt32 i = 0; i < count; i++) {
>- delete mListeners.FastObserverAt(i);
>+ delete mListeners.ObserverAt(i);
> }
> mListeners.Clear();
> return NS_OK;
> }
This should only need to be mListeners.Clear() if you use nsAutoPtr<>s.
>@@ -1152,21 +1152,21 @@ nsEventListenerManager::HandleEvent(nsPr
> }
> typeData = nsnull;
> dispData = nsnull;
> }
> }
>
> found:
>
>- nsTObserverArray<nsListenerStruct>::EndLimitedIterator iter(mListeners);
>+ nsAutoTObserverArray<nsListenerStruct*, 2>::EndLimitedIterator iter(mListeners);
Try doing |mListeners.EndLimitedIterator iter(mListeners)| here and see if that compiles. If it doesn't no worries, we'll suffer through it ;)
>Index: content/events/src/nsEventListenerManager.h
>+ nsAutoTObserverArray<nsListenerStruct*, 2> mListeners;
Make this an nsAutoTObserverArray<nsAutoPtr<nsListenerStruct>, 2>
Gotta love templates ;)
>Index: modules/libpr0n/src/imgRequest.cpp
>===================================================================
>RCS file: /cvsroot/mozilla/modules/libpr0n/src/imgRequest.cpp,v
>retrieving revision 1.101
>diff -u -8 -p -r1.101 imgRequest.cpp
>--- modules/libpr0n/src/imgRequest.cpp 14 Dec 2007 01:41:49 -0000 1.101
>+++ modules/libpr0n/src/imgRequest.cpp 18 Dec 2007 01:27:18 -0000
>@@ -326,21 +326,22 @@ void imgRequest::RemoveFromCache()
> LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
>
> if (mCacheEntry) {
> mCacheEntry->Doom();
> mCacheEntry = nsnull;
> }
> }
>
>-PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
>+PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore)
If you const-cast in the iterator ctor you should be able to keep the const here.
> NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
> {
> LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
>
> mState |= onStartDecode;
>
>- nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
>+ nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
> imgRequestProxy* proxy;
>- while ((proxy = iter.GetNext())) {
>+ while (iter.IsMore()) {
>+ proxy = iter.GetNext();
> proxy->OnStartDecode();
> }
If |proxy| isn't used after the loop here, just get rid of it entirely. Same in a bunch of places further down.
Looks great otherwise, awesome patch!
nsCSSLoader::mObservers manually addrefs and releases objects put into the array. Feel free to turn the array into an nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>> instead.
| Assignee | ||
Comment 40•17 years ago
|
||
nits fixed. builds and runs.
note that nsCSSLoader previously didn't release the elements in the mObservers array on destruction, which nsCOMPtr-ifying it will now do. i guess it relied on all its listeners removing themselves before destruction, but i thought i'd point it out (i've made the change).
thanks for all the suggestions and help sicking!
Attachment #293606 -
Attachment is obsolete: true
Attachment #293633 -
Flags: review?
| Assignee | ||
Updated•17 years ago
|
Attachment #293633 -
Flags: review? → review?(jonas)
Attachment #293633 -
Flags: superreview+
Attachment #293633 -
Flags: review?(jonas)
Attachment #293633 -
Flags: review+
| Assignee | ||
Comment 41•17 years ago
|
||
Comment on attachment 293633 [details] [diff] [review]
round 3
requesting approval1.9... this will cut thousands of allocations from startup+pageload.
Attachment #293633 -
Flags: approval1.9?
Comment 42•17 years ago
|
||
Comment on attachment 293633 [details] [diff] [review]
round 3
a=beltzner for 1.9
Attachment #293633 -
Flags: approval1.9? → approval1.9+
| Assignee | ||
Comment 43•17 years ago
|
||
patch appears to have regressed linux Ts (bl-bldlnx03) 3-4ms. i'm going to try relanding in chunks to isolate the change.
Comment 44•17 years ago
|
||
You tried to reland this right? What were the results?
| Assignee | ||
Comment 45•17 years ago
|
||
i relanded the core bits of the patch - that is, the nsTArray/nsTObserverArray changes, and the required API tweaks to consumer code, but nothing else. (i.e. leaving off the ELM nsAutoTObserverArray switch, the ELM nsAutoPtr switch, and the nsCSSLoader nsCOMPtr switch.) this part had a much smaller Ts impact (maybe 1-2ms, if any, and again only on bl-bldlnx03 - it's pretty much in the noise).
to see if i could get back that 1-2ms, i looked at the differences between nsVoidArray and nsTArray implementation, and tried two changes to nsTArray: a) switching NS_Alloc and friends to malloc (just in case), and b) changing the growth algorithm to allocate a minimum chunk of 8 entries, to match nsVoidArray. (this was my most likely guess, since using nsTArray would result in quite a few more reallocs than nsVoidArray with the thousands of tiny arrays we're probably dealing with.) a) had no effect, and i think b) probably won back that 1-2ms, but we didn't really get enough cycles to be sure. in any case, it's not something we necessarily want to do.
wrt the rest of the regression, not sure where that comes from yet. i'll try to reland pieces tonight, starting with the Auto change to ELM, and investigate.
Comment 46•17 years ago
|
||
Dan, OS/2 build breaks here
-Wp,-MD,.deps/nsTObserverArray.pp E:/usr/src/mozilla/xpcom/glue/nsTObserverArray.cpp
In file included from E:/usr/src/mozilla/xpco/glue/nsTObserverArray.cpp:39:
E:/usr/src/mozilla/xpcom/glue/nsTObserverArray.h:47: error: `typedef PRUint32 nsTObserverArray_base::size_type' is protected
E:/usr/src/mozilla/xpcom/glue/nsTObserverArray.h:354: error: within this context
Probably due that we are using gcc-3.3.5
When I make "typedef PRUint32 size_type" public it compiles. However, I don't know if that's allowed.
| Assignee | ||
Comment 47•17 years ago
|
||
thanks, i checked in a fix to make the typedef public.
alright, all of this patch is checked in now, with no perf impact... looks like it was noise, or something else going on.
in my investigations, though, i was disappointed to find that gcc on linux avoids inlining like the plague at -Os (our default). so every single method on nsTObserverArray and nsTArray get their own calls, even the trivial ones that just forward to another method. :(
this is bad for codesize and perf in this case. i wonder if we can force gcc to a different optimization level within a unit (perhaps a #pragma wrapping the relevant .h files)? in fact, a better solution would probably be to decorate a few methods with an NS_INLINE macro, that expands to __attribute__ ((always_inline)) on gcc? this might be useful in e.g. nsCOMPtr etc too. (gcc ignores the "inline" keyword at -Os, since it's just a hint.)
| Assignee | ||
Comment 48•17 years ago
|
||
in fact, we already have an NS_ALWAYS_INLINE:
http://bonsai.mozilla.org/cvsblame.cgi?file=/mozilla/xpcom/string/public/nsUTF8Utils.h&rev=1.17&mark=55-59#55
perhaps we should move that to a more fundamental header, and expand it if necessary for other plats?
cc'ing dbaron, since he added this initially.
| Assignee | ||
Comment 49•17 years ago
|
||
Mossop helped me test mac, which by default builds at -O2 (not sure if sayrer's going to change that, though). gcc inlines things properly there.
| Assignee | ||
Comment 50•17 years ago
|
||
here's a stab at using smaug's arena for allocating ELM's and nsListenerStructs. we don't always have easy access to an nsINode's allocator (e.g. from nsWindowRoot and nsGlobalWindow, the two layout/ instantiators of ELM), so in those cases we just use the global operator new/delete. i factored this logic into custom Create and Destroy methods (pretty sure we can't overload operator delete to do this, which would be nice, since there's no way to pass in mAllocator). almost all the ELMs in practice originate from content anyway, so not having an allocator for the layout ones isn't a big deal. (if there's a way to get to one, maybe via nsPIDOMWindow::GetExtantDocument() and then through to the node, i'd love to know. another possibility might be providing a single "catch-all" allocator instance on nsNodeInfoManager - stored as a static pointer in the .cpp, lazily inited and such - not sure if this is a good idea though, could get fragmented after long usage?)
Attachment #294540 -
Flags: review?
| Assignee | ||
Updated•17 years ago
|
Attachment #294540 -
Flags: review? → review?(Olli.Pettay)
Comment 51•17 years ago
|
||
nsWindowRoot and nsGlobalWindow aren't in layout/ but in dom/.
I don't see much use for a catch-all allocator - or isn't the normal
allocator (malloc) actually a 'catch-all'.
nsPIDOMWindow::GetExtantDocument could be used, it would probably
work almost always.
| Assignee | ||
Comment 52•17 years ago
|
||
(In reply to comment #51)
> nsWindowRoot and nsGlobalWindow aren't in layout/ but in dom/.
er, right, my mistake - i was thinking of the factory glue for those which lives in layout/build.
Comment 53•17 years ago
|
||
Comment on attachment 294540 [details] [diff] [review]
allocate from content arena, v1
>RCS file: /cvsroot/mozilla/content/base/src/nsDocument.cpp,v
...
>- nsresult rv = NS_NewEventListenerManager(getter_AddRefs(mListenerManager));
>+ nsresult rv = NS_NewEventListenerManager(getter_AddRefs(mListenerManager), mNodeInfo->NodeInfoManager()->NodeAllocator());
Too long line?
>+nsEventListenerManager::Create(nsDOMNodeAllocator* aAllocator)
>+{
>+ nsEventListenerManager* elm;
>+ if (aAllocator) {
>+ void* mem = aAllocator->Alloc(sizeof(nsEventListenerManager));
>+ elm = new (mem) nsEventListenerManager(aAllocator);
>+ } else {
>+ elm = new nsEventListenerManager(nsnull);
>+ }
>+ return elm;
>+}
Nit, why not
{
if (aAllocator) {
void* mem = aAllocator->Alloc(sizeof(nsEventListenerManager));
return new (mem) nsEventListenerManager(aAllocator);
}
return new nsEventListenerManager(nsnull);
}
>+nsListenerStruct*
>+nsListenerStruct::Create(nsDOMNodeAllocator* aAllocator)
Same here
Attachment #294540 -
Flags: review?(Olli.Pettay) → review+
| Assignee | ||
Comment 54•17 years ago
|
||
Comment on attachment 294540 [details] [diff] [review]
allocate from content arena, v1
thanks - nits fixed locally.
since we only hit the no-allocator codepath about 20 times when starting and creating a bunch of windows/tabs, it's probably not worth the bother to use GetExtantDocument. but if anyone can think of testcases that justify it, i can add it in.
Attachment #294540 -
Flags: superreview?(jonas)
| Assignee | ||
Comment 55•17 years ago
|
||
(In reply to comment #54)
> since we only hit the no-allocator codepath about 20 times
oops, make that ~100.
| Assignee | ||
Comment 56•17 years ago
|
||
regarding comment 48, i think the best thing to do is just have the compiler inline things sanely (tall order for gcc?). filed bug 409803 to do that.
Comment on attachment 294540 [details] [diff] [review]
allocate from content arena, v1
>Index: content/base/src/nsContentUtils.cpp
>===================================================================
>RCS file: /cvsroot/mozilla/content/base/src/nsContentUtils.cpp,v
>retrieving revision 1.263
>diff -u -8 -p -r1.263 nsContentUtils.cpp
>--- content/base/src/nsContentUtils.cpp 18 Dec 2007 21:24:54 -0000 1.263
>+++ content/base/src/nsContentUtils.cpp 25 Dec 2007 20:47:58 -0000
>@@ -3118,17 +3118,18 @@ nsContentUtils::GetListenerManager(nsINo
> PL_DHASH_ADD));
>
> if (!entry) {
> return NS_ERROR_OUT_OF_MEMORY;
> }
>
> if (!entry->mListenerManager) {
> nsresult rv =
>- NS_NewEventListenerManager(getter_AddRefs(entry->mListenerManager));
>+ NS_NewEventListenerManager(getter_AddRefs(entry->mListenerManager),
>+ aNode->GetOwnerDoc()->NodeInfoManager()->NodeAllocator());
Actually, wouldn't it be better to use the nodes allocator rather than the nodes ownerdocs allocator? Seems like the listenermanager will always have the same lifetime as the node, even if the node is moved between documents, potentially multiple times.
Comment 58•17 years ago
|
||
Moving to blocking list so we can wrap up footprint work...
Flags: blocking1.9+
Priority: -- → P1
| Assignee | ||
Comment 59•17 years ago
|
||
with nits fixed. carrying over r=smaug.
Attachment #294540 -
Attachment is obsolete: true
Attachment #296408 -
Flags: superreview?(jonas)
Attachment #296408 -
Flags: review+
Attachment #294540 -
Flags: superreview?(jonas)
| Assignee | ||
Comment 60•17 years ago
|
||
sicking - you mentioned you wanted to wait on this a bit until after MH work, or somesuch - do we want this for b3?
Yes we do. We should get bug 408720 fixed first though.
Depends on: 408720
Comment on attachment 296408 [details] [diff] [review]
v2
>Index: content/events/public/nsIEventListenerManager.h
>-NS_NewEventListenerManager(nsIEventListenerManager** aInstancePtrResult);
>+NS_NewEventListenerManager(nsIEventListenerManager** aInstancePtrResult,
>+ nsDOMNodeAllocator* aAllocator = nsnull);
I'm not a big fan of default arguments unless there are lots of callsites that would get significantly more readable by not specifying the argument.
Looks good otherwise, but please wait with landing this until bug 408720 is fully landed.
Attachment #296408 -
Flags: superreview?(jonas) → superreview+
Comment 63•17 years ago
|
||
One idea to reduce the size of ELM. The allocator could be get from mTarget, when
mTarget is nsINode. When setting mTarget to null, mTarget could be set to
point to allocator. Some bit handling/unions needed to implement this, but should
work.
Stuart says that the continued life of the content arena is uncertain still. So please hold off landing this until you've talked to him.
Comment 65•17 years ago
|
||
Dumb question - why is this bug blocked on the content arena bug?
To give us an opportunity to fix the regression before we start muddy the numbers by putting more stuff in the arena.
| Assignee | ||
Comment 67•17 years ago
|
||
stuart said we may end up backing out content arenas, so i'm going to hold off on this for now. we can land after b3 if necessary.
| Assignee | ||
Comment 69•17 years ago
|
||
Comment on attachment 296408 [details] [diff] [review]
v2
obsolete per bug 414894. we'll go full-circle back to the original suggestion of nsAutoTObserverArray<nsListenerStruct, 2> here.
Attachment #296408 -
Attachment is obsolete: true
| Assignee | ||
Comment 70•17 years ago
|
||
as advertised.
Attachment #300994 -
Flags: superreview?(jonas)
Attachment #300994 -
Flags: review?(jonas)
Comment on attachment 300994 [details] [diff] [review]
store struct instance, v1 (checked in)
>@@ -183,22 +182,22 @@ protected:
> PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget,
> nsIPresShell* aShell,
> nsPoint& aTargetPt);
> void GetCoordinatesFor(nsIDOMElement *aCurrentEl, nsPresContext *aPresContext,
> nsIPresShell *aPresShell, nsPoint& aTargetPt);
> nsresult GetDOM2EventGroup(nsIDOMEventGroup** aGroup);
> PRBool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent);
>
>- nsAutoTObserverArray<nsAutoPtr<nsListenerStruct>, 2> mListeners;
>- nsISupports* mTarget; //WEAK
>- PRUint32 mMayHaveMutationListeners : 1;
>+ nsAutoTObserverArray<nsListenerStruct, 2> mListeners;
>+ nsISupports* mTarget; //WEAK
>+ PRUint32 mMayHaveMutationListeners : 1;
> // These two member variables are used to cache the information
> // about the last event which was handled but for which event listener manager
> // didn't have event listeners.
>- PRUint32 mNoListenerForEvent : 31;
>- nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
>+ PRUint32 mNoListenerForEvent : 31;
>+ nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
>
>- static PRUint32 mInstanceCount;
>- static jsval sAddListenerID;
>+ static PRUint32 mInstanceCount;
>+ static jsval sAddListenerID;
Can we please stop this insanity and not keep members aligned. It just makes patches impossible to read :(
r/sr=me either way
Attachment #300994 -
Flags: superreview?(jonas)
Attachment #300994 -
Flags: superreview+
Attachment #300994 -
Flags: review?(jonas)
Attachment #300994 -
Flags: review+
Comment 72•17 years ago
|
||
Or don't mix style fixes into substantive fixes. Or, beware aligning all members when only one bad boy type is pushing the declarator or declarator name way over (too far to be a good layout if that overlong type were not added). Let the one long type push its member over, leave the rest sanely aligned on a lower column number.
/be
This is ready for check-in, right?
| Assignee | ||
Updated•17 years ago
|
Attachment #300994 -
Attachment description: store struct instance, v1 → store struct instance, v1 (checked in)
| Assignee | ||
Comment 74•17 years ago
|
||
-> fixed, since we've done about all we can here. (we could perhaps use a fixed-size allocator for ELMs themselves, but meh - we've got most of the benefit here already)
looks like A went down by a few K after checkin.
Status: ASSIGNED → RESOLVED
Closed: 17 years ago
Resolution: --- → FIXED
Comment 75•16 years ago
|
||
Note that the PrependElementUnlessExists method added here was broken: it didn't adjust iterator indices. The patch in bug 474369 should fix it.
Comment 76•16 years ago
|
||
And in particular, going from round 2 to round 3 of the nsTObserverArray patch lost the iterator index adjustment code.
Updated•6 years ago
|
Component: Event Handling → User events and focus handling
You need to log in
before you can comment on or make changes to this bug.
Description
•