Closed Bug 203699 Opened 22 years ago Closed 15 years ago

fully unacceptable JavaScript performace with Germany's most famous household appliances company's homepage

Categories

(Core :: DOM: Core & HTML, defect)

defect
Not set
major

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: franz.gans, Unassigned)

References

()

Details

(Keywords: perf, testcase)

Attachments

(7 files)

User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) Build Identifier: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.3) Gecko/20030312 Browse their homepage, please! Reproducible: Always Steps to Reproduce: 1. http://www.aeg-hausgeraete.de/ 2. click and see how mozilla freezes 3. Actual Results: Mozilla needs 17 (seventeen) seconds to display the page. While it is working, every Mozilla window freezes. Where is the multi-threading? Expected Results: It should be effective and efficient.
[Bugzilla: does not send email?]
no freeze with win2k build 20030426.. (or <1sec)
Confirming bug with Mozilla trunk binary 2003042808 on WinNT4.0. OS: Linux ---> All. The site takes about 16 seconds to load for me on WinNT. What seems to take all the time is the document.write() of the menu frame at the left. After 16 seconds elapse, suddenly the content of the menu appears. I can't figure out what's going wrong, but it doesn't seem to be due to pure JS Engine performance. The testcase I'll attach below will show this. The site uses this frameset structure: <frameset> <frame name="menu" src="node10.asp"> <frameset> <frame name="topFrame" src="node11.asp"> <frame name="main" src="node692.asp"> </frameset> </frameset> The "menu" frame has this body onload handler: <body onLoad="parent.doMenu()"> The doMenu() function creates an abstract 133-element array as follows: function doMenu() { var i=0; arrMenuItems[i++] = new createMenuItem(692,9,"Home",1,"node692.asp",""); arrMenuItems[i++] = new createMenuItem(680,9,"Neuheiten",1,"node680.asp",""); arrMenuItems[i++] = new createMenuItem(371,9,"Produkte",1,"node371.asp",""); etc. etc. } where the function createMenuItem() is simply: function createMenuItem(id,pid,name,level,URL,target) { this.id = id; this.pid = pid; this.name = name; this.level = level; this.URL = URL; this.target = target; } Meanwhile, the "main" frame has this body onload handler: <body onLoad="fixMenu(692);" > The 692 simply serves to highlight the first menu item (see above array). This function, located in the frame's own script, calls up to the parent: function fixMenu(ID) { if(parent.fixMenu) { if(ID) parent.fixMenu(ID) } } That is, the frame calls the fixMenu() function of the parent page. In the testcase, I will only have this parent version of the function. It document.writes() the menu into the "menu" frame by using the arrMenuItems[] array above, and this sequence of function calls: fixMenu() ---> prepareMenu() ---> buildMenu() It's this last function, buildMenu(), that is causing the trouble. It gets called 133 times, one for each element of arrMenuItems[]. Furthermore, it calls itself recursively, to recursion depth = 4. I did a performance profile of the site using Venkman, Mozilla's JavaScript debugger. I also did one for the reduced testcase I'll attach below. The two profiles show a big difference in the time the buildMenu() function takes in each case -
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: perf
OS: Linux → All
The reduced testcase will have this frameset structure: <frameset> <frame name="menu" src="about:blank" onload="doMenu();fixMenu(692);"> <frameset> <frame name="topFrame" src="about:blank"> <frame name="main" src="about:blank"> </frameset> </frameset> That is, all the frames have src="about:blank", and the calls to doMenu() and fixMenu() are made directly from the onload handler of the "menu" frame. I only keep the other frames there to retain the geometry of the page layout -
Attached file Reduced testcase #1
When you look at the Venkman profiles, don't forget the call sequence above: fixMenu() ---> prepareMenu() ---> buildMenu() Therefore large execution times for the first two functions are simply due to the large execution time for buildMenu(). This is the biggest difference between the two profiles: ------------------------- At the site ------------------------- Function Name: buildMenu (Lines 115 - 163) Total Calls: 133 (max recurse 4) Total Time: 15703.13 (min/max/avg 15703.13/15703.13/118.07) ----------------------- In the testcase ----------------------- Function Name: buildMenu (Lines 242 - 290) Total Calls: 133 (max recurse 4) Total Time: 531.25 (min/max/avg 531.25/531.25/3.99) You can see that in each case, the function is called 133 times with a recursion depth of 4. The total time this function takes at the website is 15,703 ms ~ 15.7 seconds, with an average of 118 ms per call. By contrast, the total time it takes in the testcase is ~ 531 ms, with an average of 4 ms per call. So for some reason, this function is taking approx. 30 times longer when called on the website than it does when called in the testcase. I do not know why that is, other than the change in frameset structure that I made, or possible security domain checks that may be occurring when Mozilla is loading the website. At any rate, the pure JavaScript involved seems to be the same in each case. One has to be careful here, and look at every logical step that buildMenu() makes. For example, I verified |arrParents.length| === 1 at both the website and the testcase. Therefore in each case, we fall into the |(arrParents.length > 0)| branch of the buildMenu() function. One can verify this by loading each page and entering: javascript: alert(arrParents.length); Reassigning to DOM Level 0 for further analysis; cc'ing Mitch -
Assignee: rogerl → dom_bugs
Component: JavaScript Engine → DOM Level 0
QA Contact: pschwartau → ashishbhatt
To be precise, it is the prepareMenu() function that does the document.write() into the menu frame: menu.document.writeln(strStart + strHTML + strEnd); The |strStart|, |strEnd| variables are defined inside the prepareMenu() function, but without the |var| keyword, and are therefore global variables. The |strHTML| variable is declared at the top of the parent script, so is also global. It is this string variable that is set by the buildMenu() function, called from prepareMenu().
Summary: fully unacceptable java script performace with Germany's most famous household appliances company's homepage → fully unacceptable JavaScript performace with Germany's most famous household appliances company's homepage
Other points to note: 1. There are .gif images that load to the left of the menu items. In the testcase, I gave them absolute URLs. At the website, they have relative URLs. 2. The "main" frame includes a Shockwave Flash plug-in. The URL for this frame is http://www.aeg-hausgeraete.de/node692.asp
I keep worrying that somehow not all codepaths in the buildMenu() function are being exercised in the reduced testcase as they would be at the website. To investigate this, I used a more granular form of Venkman logging. I set a breakpoint at the following line inside buildMenu(): if(arrParents[i] == arrMenuItems[j].pid) This is at line 122 at the website, and at line 249 in the testcase. Once this breakpoint is hit, I right-click this line in Venkman and set these breakpoint properties: Enable breakpoint: OFF When triggered, execute: return __count__++; Continue regardless of result: ON Log result ON I then double-click on "Apply" and hit "F5" to continue. This takes time, as the line should be hit 133*133 = 17,689 times! I found that it was hit exactly this number of times, whether at the website or the testcase. So at least this part of the codepath is exercised the same in each. There are other lines to check as well; if I have time I will do that -
That's some pretty fancy breakpointing :) I think I'll add this to the FAQ as a way to log specific functions. I'd just like to point out two things: One, __count__ is a paremeter of the breakpoint script, so it goes away when the function returns. Venkman maintains the breakpoint count elsewhere, and just passes the correct value to the breakpoint script. The upshot of this is that |return __count__++;| should just be |return __count__;|. The second thing is that the "Enable Breakpoint" option is actually supposed to disable the breakpoint altogether. "Continue regardless of result" is all you should have needed to check to keep venkman from actually *stopping* for the breakpoint. The fact that the breakpoint still happened means that there is a bug in Venkman. I'll look into it.
Ah, the "Enable Breakpoint" problem was also the reason you had to double-click the Apply button, instead of just single click it. Both problems are fixed in 0.9.67, at http://www.hacksrus.com/~ginda/venkman/.
This is caused by bug 169559. Try this: 1. Save some files locally. You need http://www.aeg-hausgeraete.de/ , http://www.aeg-hausgeraete.de/node10.asp and http://www.aeg-hausgeraete.de/node692.asp to pollute the global namespace sufficiently (I think that's why the testcase is not as slow). The 2 other files need access to the main page, so they must be local too. 2. Put a <base href="http://www.aeg-hausgeraete.de/"> in every file and adjust the frameset in the main page to point to your local files. 3. Load the page to make sure it's still slow. 4. Add a few missing |var|s in the script of the main page (at least for |arrMenuItems| and |arrParents|; there are more, but they are less important) and wrap the script within this code: (function(){ // original script goes here self.doMenu= doMenu; self.fixMenu= fixMenu; })(); 5. Load the page again. It should be quite fast now. 6. Just to be sure, remove the |var|s added in step 4. The page is slow again.
Depends on: 169559
Keywords: testcase
Hardware: PC → All
No longer depends on: 169559
Depends on: 169559
> Put a <base href="http://www.aeg-hausgeraete.de/"> in every file and > adjust the frameset in the main page to point to your local files. In Mozilla trunk binary 2003042808 on WinNT, I can't run the original HTML locally with these <base> tags added. I get errors in the JavaScript Console like this: Uncaught Exception: Permission denied to call Window.doMenu() Uncaught Exception: Permission denied to call Window.fixMenu() If I leave the <base> tags out, I can still see the long page-loading time problem, even if the images don't all resolve properly. zack-weg@gmx.de: can you say more about the <base> tags? Did you add them to the parent frameset page as well as to each frame page? What |src| did you use for the frames inside the frameset page? Fully-qualified file:/// URLs, or relative URLs? If I have a <base> tag at the top of the frameset page, and I use relative URLS for the frames, Mozilla pulls the frames from http://www.aeg-hausgeraete.de/ and gives me the above errors. This is due to cross-domain security code in Mozilla: it refuses to allow the frames off the Internet to call the functions in the parent frameset page, which are in my local hard drive. On the other hand, if I use fully-qualified file:/// URLs for the frames, then <base> tags in each frame seem to confuse Mozilla. I may be doing something wrong - the only thing that works for me is to not use any <base> tags at all -
Sorry for the confusion, I've tested with a very old build (shouldn't do that, I know), and behavior of security checks for docs with <base> has changed (I consider the new behavior to be a bug, but it seems to be meant as a feature -- at least the opposite is not true and a page cannot pretend to be local). So indeed you cannot use <base> for the main page, but there is no need for it. You can use an absolute URL for the third frame instead or just replace it with about:blank, it's not important. But while testing this I noticed that it's enough to change your testcase to move the fixMenu() call in its own file (and of course remove the <base> too). I'll set up a testcase.
zack-weg@gmx.de: thanks, you have solved the problem completely!!! The attachment in Comment #19 takes a long time to load in Mozilla. The attachment in Comment #20 does not. The only difference is the wrapping of the global variables in Comment #20 to make them local. Brilliant! This shows it's definitely the Mozilla DOM security checks on global variables that slow the website down.
Another note: The two onload handlers contain a race condition. |fixMenu| calls |doMenu| again if the other handler hasn't started to construct |arrMenuItems| yet. And then they might construct |arrMenuItems| concurrently.
*** Bug 204529 has been marked as a duplicate of this bug. ***
Assignee: general → nobody
QA Contact: ashshbhatt → general
This is a very old bug, and the website that was a problem seems to have changed considerably since it was reported, but it loads quickly for me in Firefox 3.6 and so do all the testcases, so I'm closing this. Please reopen if it's still a problem for you, and give details.
Status: NEW → RESOLVED
Closed: 15 years ago
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: