I've noticed that the performance of my web apps is very slow when dealing with large amounts of data (e.g. generating >100 tree nodes, each of which includes InPlaceEditors, Tooltips (my own using Prototype/SAU), lots of various nodes built with Builder.node, etc...). I started using the new Firebug 1.0 beta's profiling and discovered a few things. 1) Builder.node calls escapeHTML on all attribute values when parsing the attributes 2) escapeHTML creates a div node and a text node each time it is called 3) The combination of 1) and 2) yields a massive performance hit
So what is the reason for 1)? I don't know of any *standard* attribute values offhand that use special HTML characters, nor can I think of a good reason to. I don't think it'd be too much to ask for the programmer to realize when he may be using HTML characters and have the foresight to add .escapeHTML to the string when it is passed. Maybe there is a common case that makes this worth while that I don't know of? ------------For reference---------- _attributes: function(attributes) { var attrs = []; for(attribute in attributes) attrs.push((attribute=='className' ? 'class' : attribute) + '="' + attributes[attribute].toString().escapeHTML() + '"'); return attrs.join(" "); }, -----------------------------------
On 2), why can't the escapeHTML function create a div node and a text node once one the first call and reuse it on subsequent calls? Also, is this not a leak? ------------For reference--------- escapeHTML: function() { var div = document.createElement('div'); var text = document.createTextNode(this); div.appendChild(text); return div.innerHTML; }, ----------------------------------
> On 2), why can't the escapeHTML function create a div node and a > text node once one the first call and reuse it on subsequent > calls? Also, is this not a leak? > ------------For reference--------- > escapeHTML: function() { > var div = document.createElement('div'); > var text = document.createTextNode(this); > div.appendChild(text); > return div.innerHTML; > }, > ----------------------------------
That's something we'd need to measure. The text node creation wouldn't go away, and reusing the div node would mean to empty it out before adding the text node. I don't really think that it would make a real difference.
Thomas Fuchs wrote: > Am 04.01.2007 um 13:19 schrieb Colin Mollenhour: >> On 2), why can't the escapeHTML function create a div node and a text >> node once one the first call and reuse it on subsequent calls? Also, >> is this not a leak?
a) this is veeeery browser dependent :( b) leak? can't see a reason why.
> That's something we'd need to measure.
Let's measure. Note that I threw in two more "old school" methods (string manipulation based).
Great page, that is a cool utility that I'm sure I will use in the future!
You've shown the "old school" methods are clearly faster for the top browsers (FF,IE6/7) which I think would make the change to String.replace or String.split worthwhile. (I was able to reproduce your results, only mine were a lot faster across the board, what CPU are you using?) Even though String.replace is slower in Opera I'd be in favor of it since Opera clearly has some room for improvement with that function so I'm guessing this may be improved in the future.
> Thomas Fuchs wrote: >> Am 04.01.2007 um 13:19 schrieb Colin Mollenhour: >>> On 2), why can't the escapeHTML function create a div node and a text >>> node once one the first call and reuse it on subsequent calls? Also, >>> is this not a leak? > a) this is veeeery browser dependent :( > b) leak? can't see a reason why.
>> That's something we'd need to measure. > Let's measure. Note that I threw in two more "old school" methods > (string manipulation based).
So Marius debunked my "improvement" to escapeHTML but did prove that other methods can be much faster.
However, what are your thoughts on removing the usage of escapeHTML from _attributes altogether? I think Builder.node needs to be optimized for speed as much as possible since it is something that could be called thousands of times easily. I will be looking into the usage of cloneNode to help alleviate this, but the code probably won't be as neat and I won't always be able to use cloneNode.
> Am 04.01.2007 um 13:19 schrieb Colin Mollenhour:
>> On 2), why can't the escapeHTML function create a div node and a >> text node once one the first call and reuse it on subsequent calls? >> Also, is this not a leak? >> ------------For reference--------- >> escapeHTML: function() { >> var div = document.createElement('div'); >> var text = document.createTextNode(this); >> div.appendChild(text); >> return div.innerHTML; >> }, >> ----------------------------------
> That's something we'd need to measure. The text node creation > wouldn't go away, and reusing the div node would mean to empty it out > before adding the text node. I don't really think that it would make > a real difference.
Hi, If not removing it completely, I would go for a way (options, second version etc) to turn it off. Sometimes I even think of a global flag for that. Have been playing around with symfony now for a while. I guess Ruby includes it same way. Shouldn't in the framework case the framework escape everything while generating the Javscript code? So I would go further and say that scriptaculous can even expect getting escaped text. But since there are manual users who might need it I would like to have a switch for it, as it is, like Colin mentioned, a big hit there.
-----Original Message----- From: Colin Mollenhour Sent: Samstag, 6. Januar 2007 03:36 To: rubyonrails-spinoffs@googlegroups.com Subject: [Rails-spinoffs] Re: SAU/Prototype performance
So Marius debunked my "improvement" to escapeHTML but did prove that other methods can be much faster.
However, what are your thoughts on removing the usage of escapeHTML from _attributes altogether? I think Builder.node needs to be optimized for speed as much as possible since it is something that could be called thousands of times easily. I will be looking into the usage of cloneNode to help alleviate this, but the code probably won't be as neat and I won't always be able to use cloneNode.
Colin Mollenhour wrote: > (I was able to reproduce your results, only mine were a lot faster > across the board, what CPU are you using?)
As already mentioned, I run all the tests with ten thousands iterations (vs the default "one thousand" value which I suppose you used too).
My tests were done on a very "experimentalish" Linux system, host processors are "Intel(R) Pentium(R) 4 CPU 3.00GHz", each guest host having 512MB RAM allocated (no swapping occured during tests).
Sorry about this misleading "trick", 1K is a safe default for people who want to get a quick glance, but I used a larger samples number to get more trustworthy results. OFC, I could have used even more iterations, but 10K was enough to get a smooth variation between different executions (at least for those "Prototypish" methods). For instance, a 100K run on Firefox will show similar results (comparing to previous rating):
> Even though String.replace is slower in Opera I'd be in favor of it > since Opera clearly has some room for improvement with that function > so I'm guessing this may be improved in the future.
Hm, as previously stated, browsers behave very different, and Sébastien's tests just completed the picture. Thanks. I wonder, even after running all these tests, which would be the really best overall choice? I for one cannot clearly see a winner. OFC, YMMV.
I remember I worked long time ago on a tool that consolidates different runs of the same benchmark, I guess it could help us getting a better overview... but I'm a mess, have no idea about the when, where or for. Anyway, should be simple enough to write from scratch (maybe even in JavaScript)... we'll see. ;-)
Browser dependent implementations could be a choice, but really!?! Is this the way to go? I really doubt it, and I'm sure many will second me on this one, based on previous conversation, both on this list and on our bug tracker.
Sure, we should work on optimizing things, but I'm not too sure about this particular case ("escapeHTML"). In this case, maybe settling to your previous solution (avoiding escapeHTML for Builder.node attributes) would be a safer resolution for now, even if it's plain wrong (purist talk here: if one test fails, everything should get back to the drawing board). I know, web developers are not generally like that, as they're getting disappointed every day by existent "platforms".
cheers. - -- Marius Feraru -----BEGIN PGP SIGNATURE-----
Marius Feraru wrote: > I remember I worked long time ago on a tool that consolidates different > runs of the same benchmark, I guess it could help us getting a better > overview... but I'm a mess, have no idea about the when, where or for. > Anyway, should be simple enough to write from scratch (maybe even in > JavaScript)... we'll see. ;-)
Hm, reply to myself, not good. But anyway, curiosity killed the cat. So I baked a quick hack to see what are we talking about. Collin, prepare to laugh :)
A. Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061222 Firefox/2.0.0.1 B. Opera/9.10 (X11; Linux i686; U; en) C. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; FDM) D. Mozilla/4.0 (compatible; MSIE 6.0; Windows 98) E. Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) F. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv: 1.8.1.1) Gecko/20061204 Firefox/2.0.0.1 G. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/ 418.9.1 (KHTML, like Gecko) Safari/419.3
Method execution rate (per second) per browser: Method A B C D E F G Prototype 3535 18051 4355 4303 3929 9737 27397 Prototype-oneDiv 2635 17575 5294 5679 4070 7107 25974 String.replace 15625 9242 15974 20325 3660 28571 16920 String.split 7675 28169 9009 7407 3911 15432 23753
Top method per browser: Method A B C D E F G AVG Prototype 3 2 4 4 2 3 1 2.71 Prototype-oneDiv 4 3 3 3 1 4 2 2.86 String.replace 1 4 1 1 4 1 4 2.29 String.split 2 1 2 2 3 2 3 2.14
Collin, checkout the average column (AVG)? :)
So, String.replace gets most tops, but also gets most bottoms. :)
The funniest thing is that String.split looks like being the best choice. UGLY! I always hated that solution (for its ugliness if nothing else) ;-)
After more brainstorming, we (mishoo and I) came up with some updates to my previous test, the most important thing being that we have a new winner :)
We baked two more methods: String.replaceMap (a sloppy variant of String.replace), and Prototype.oneDiv2 (a variant of Prototype.oneDiv which does caches the textNode too).
More updates: - - change text after each test (adding/changing trailing digits - to avoid any hypothetical browsers' cache tricks); - - dropped escaping """ (to play similar to "oneDiv" methods); - - changed default iterations number to "10000" (to avoid future confusion).
OK, without further ado, here are the consolidated results:
Benchmarks run using 10000 iterations on these browsers: A. Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061222 Firefox/2.0.0.1 B. Opera/9.10 (X11; Linux i686; U; en) C. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; FDM) D. Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) E. Mozilla/4.0 (compatible; MSIE 6.0; Windows 98) F. Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8.1 (KHTML, like Gecko) Safari/312.6 G. Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Method execution rate (per second) per browser: Method A B C D E F G Prototype 3197 17094 4182 3959 4704 3598 3162 Prototype.oneDiv 2548 16529 5005 4006 5283 5851 1826 Prototype.oneDiv2 8170 51020 13405 8258 10953 13477 6588 String.replace 15175 6739 15699 4468 15949 1604 10526 String.replaceMap 8905 9699 12804 4008 16584 1694 6540 String.split 10163 27322 9681 4983 10373 1730 6954
Top method per browser: Method A B C D E F G AVG Prototype 5 3 6 6 6 3 5 4.86 Prototype.oneDiv 6 4 5 5 5 2 6 4.71 Prototype.oneDiv2 4 1 2 1 3 1 3 2.14 String.replace 1 6 1 3 2 6 1 2.86 String.replaceMap 3 5 3 4 1 5 4 3.57 String.split 2 2 4 2 4 4 2 2.86
Sorry, I have no Intel based Mac available at this hour, maybe Sébastien could help us. ;-)
cheers - -- Marius Feraru -----BEGIN PGP SIGNATURE-----
Marius Feraru wrote: > Sorry, I have no Intel based Mac available at this hour, maybe Sébastien > could help us. ;-)
Talked too soon, my folks are still alive ;-)
Benchmarks run using 10000 iterations on these browsers: A. Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061222 Firefox/2.0.0.1 B. Opera/9.10 (X11; Linux i686; U; en) C. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; FDM) D. Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) E. Mozilla/4.0 (compatible; MSIE 6.0; Windows 98) F. Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8.1 (KHTML, like Gecko) Safari/312.6 G. Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0 H. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1 I. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.2
Method execution rate (per second) per browser: Method A B C D E F G H I Prototype 3197 17094 4182 3959 4704 3598 3162 8803 24096 Prototype.oneDiv 2548 16529 5005 4006 5283 5851 1826 6017 20833 Prototype.oneDiv2 8170 51020 13405 8258 10953 13477 6588 16667 39841 String.replace 15175 6739 15699 4468 15949 1604 10526 32051 20619 String.replaceMap 8905 9699 12804 4008 16584 1694 6540 15221 24213 String.split 10163 27322 9681 4983 10373 1730 6954 17241 27548
Top method per browser: Method A B C D E F G H I AVG Prototype 5 3 6 6 6 3 5 5 4 4.78 Prototype.oneDiv 6 4 5 5 5 2 6 6 5 4.89 Prototype.oneDiv2 4 1 2 1 3 1 3 3 1 2.11 String.replace 1 6 1 3 2 6 1 1 6 3.00 String.replaceMap 3 5 3 4 1 5 4 4 3 3.56 String.split 2 2 4 2 4 4 2 2 2 2.67
So this new method really looks like being our winner ;-)
Excellent work! The oneDiv2 function really does shine but considering the prevalence of FF and IE6/7, I would still be hard pressed to choose it over String.replace. Either way, they are both a big improvement over the original Prototype method (with the exception of String.replace for Opera and Intel Safari). Take the last step and submit a patch with your results to back it up :)
> Marius Feraru wrote: >> Sorry, I have no Intel based Mac available at this hour, maybe S bastien >> could help us. ;-) > Talked too soon, my folks are still alive ;-)
> Benchmarks run using 10000 iterations on these browsers: > A. Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20061222 > Firefox/2.0.0.1 > B. Opera/9.10 (X11; Linux i686; U; en) > C. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR > 2.0.50727; FDM) > D. Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like > Gecko) > E. Mozilla/4.0 (compatible; MSIE 6.0; Windows 98) > F. Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8.1 > (KHTML, like Gecko) Safari/312.6 > G. Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) > Gecko/20061010 Firefox/2.0 > H. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) > Gecko/20061204 Firefox/2.0.0.1 > I. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418 > (KHTML, like Gecko) Safari/417.9.2
> Method execution rate (per second) per browser: > Method A B C D E F G > H I > Prototype 3197 17094 4182 3959 4704 3598 3162 > 8803 24096 > Prototype.oneDiv 2548 16529 5005 4006 5283 5851 1826 > 6017 20833 > Prototype.oneDiv2 8170 51020 13405 8258 10953 13477 6588 > 16667 39841 > String.replace 15175 6739 15699 4468 15949 1604 10526 > 32051 20619 > String.replaceMap 8905 9699 12804 4008 16584 1694 6540 > 15221 24213 > String.split 10163 27322 9681 4983 10373 1730 6954 > 17241 27548
> Top method per browser: > Method A B C D E F G > H I AVG > Prototype 5 3 6 6 6 3 5 > 5 4 4.78 > Prototype.oneDiv 6 4 5 5 5 2 6 > 6 5 4.89 > Prototype.oneDiv2 4 1 2 1 3 1 > 3 3 1 2.11 > String.replace 1 6 1 3 2 6 1 > 1 6 3.00 > String.replaceMap 3 5 3 4 1 5 > 4 4 3 3.56 > String.split 2 2 4 2 4 4 2 > 2 2 2.67
> So this new method really looks like being our winner ;-)
> - -- > Marius Feraru > -----BEGIN PGP SIGNATURE-----
Colin Mollenhour wrote: > The oneDiv2 function really does shine but considering the prevalence of > FF and IE6/7, I would still be hard pressed to choose it over > String.replace.
And if we notice how different FF behaves on each platform (comparing to Opera for instance), the choice becomes even more difficult, at least if we would have chosen to implement different solutions for each browser/platform.
> Take the last step and submit a patch with your results to back it up :)
Colin Mollenhour wrote: > Excellent work! The oneDiv2 function really does shine but considering > the prevalence of FF and IE6/7, I would still be hard pressed to choose > it over String.replace.
Also consider that while innerHTML has been widely copied, it has no public standard whereas String.replace does. It is known that different browsers will return different innerHTML given identical HTML source or piece of DOM tree. Having it as the basis for a kind of 'normalise' function doesn't make sense - speed should not be the sole criterion.
> Marius Feraru wrote: >> Sorry, I have no Intel based Mac available at this hour, maybe >> Sébastien >> could help us. ;-) > Talked too soon, my folks are still alive ;-)
> Benchmarks run using 10000 iterations on these browsers: > A. Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/ > 20061222 Firefox/2.0.0.1 > B. Opera/9.10 (X11; Linux i686; U; en) > C. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR > 2.0.50727; FDM) > D. Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like > Gecko) > E. Mozilla/4.0 (compatible; MSIE 6.0; Windows 98) > F. Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8.1 > (KHTML, like Gecko) Safari/312.6 > G. Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) > Gecko/20061010 Firefox/2.0 > H. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) > Gecko/20061204 Firefox/2.0.0.1 > I. Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418 > (KHTML, like Gecko) Safari/417.9.2
> Method execution rate (per second) per browser: > Method A B C D E F > G H I > Prototype 3197 17094 4182 3959 4704 3598 3162 > 8803 24096 > Prototype.oneDiv 2548 16529 5005 4006 5283 5851 1826 > 6017 20833 > Prototype.oneDiv2 8170 51020 13405 8258 10953 13477 6588 > 16667 39841 > String.replace 15175 6739 15699 4468 15949 1604 10526 > 32051 20619 > String.replaceMap 8905 9699 12804 4008 16584 1694 6540 > 15221 24213 > String.split 10163 27322 9681 4983 10373 1730 6954 > 17241 27548
> Top method per browser: > Method A B C D E F > G H I AVG > Prototype 5 3 6 6 6 3 > 5 5 4 4.78 > Prototype.oneDiv 6 4 5 5 5 2 > 6 6 5 4.89 > Prototype.oneDiv2 4 1 2 1 3 1 > 3 3 1 2.11 > String.replace 1 6 1 3 2 6 > 1 1 6 3.00 > String.replaceMap 3 5 3 4 1 5 > 4 4 3 3.56 > String.split 2 2 4 2 4 4 > 2 2 2 2.67
> So this new method really looks like being our winner ;-)
> - -- > Marius Feraru > -----BEGIN PGP SIGNATURE-----