UNCONFIRMED 20929
Major performance degradation when border is added to element
https://bugs.webkit.org/show_bug.cgi?id=20929
Summary Major performance degradation when border is added to element
John Engelhart
Reported 2008-09-18 17:25:58 PDT
I've found a major performance degradation problem when a border is added. Machine: 1.5ghz powerbook OS: Mac OS X 10.5.4 Safari: Version 4.0 (5528.1, r36519) Shark: 4.6.1 (227) I ran in to this while redesigning a home page and I've managed to create a test case that reproduces the problem (see attached, tarball with two .html files, topbar.html and topbar_slow.html). There is a single line difference between the fast and slow versions: near the top, in the <style> section, a single CSS attribute is uncommented in the _slow version, specifically border: 1px solid #f00; That's the only difference between the two. I've also included the shark profiles from my machine (1.5ghz g4 powerbook running 10.5.4). I tried to do the exact same sequence of steps during the sampling of both profiles. The sequence of steps was: Move over the left, 'regexkit' Download Now button. Move over the right 'regexkitlite' Download Now button. Move up to 'sourceforge.net' text. Move right to 'Contact' text. Move up to search box. Move right to left once over 'Documentation Download Forums Tickets' The page was reloaded, the sequence performed once to 'warm up' any type of caching, sampling started, repeat sequence, stop sampling. Here are the top 5 entries from the heavy / bottom-up traces for the fast no-border and slow border .html pages. The values are in absolute milliseconds since the percentages hide the relative magnitude of the problem. The "fast" profile: # Report 5 - shark_topbar.mshark - Time Profile of Safari SharkProfileViewer # Generated from the visible portion of the outline view - 158.1 ms, 0x7ce8 [216B], Safari - 99.6 ms, WebCore::GeneratedImage::draw(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::FloatRect const&, WebCore::CompositeOperator), WebCore - 85.7 ms, 0x1ff48 [92B], Safari - 37.7 ms, WebCore::GraphicsContext::fillRoundedRect(WebCore::IntRect const&, WebCore::IntSize const&, WebCore::IntSize const&, WebCore::IntSize const&, WebCore::IntSize const&, WebCore::Color const&), WebCore - 35.1 ms, WebCore::GraphicsContext::fillRect(WebCore::FloatRect const&, WebCore::Color const&), WebCore # Report 6 - shark_topbar_slow.mshark - Time Profile of Safari SharkProfileViewer # Generated from the visible portion of the outline view - 459.7 ms, WebCore::Image::drawPattern(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::AffineTransform const&, WebCore::FloatPoint const&, WebCore::CompositeOperator, WebCore::FloatRect const&), WebCore - 266.3 ms, WebCore::GeneratedImage::drawPattern(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::AffineTransform const&, WebCore::FloatPoint const&, WebCore::CompositeOperator, WebCore::FloatRect const&), WebCore - 179.4 ms, 0x7ce8 [216B], Safari - 77.9 ms, 0x1ff48 [92B], Safari - 45.8 ms, WTF::tryFastCalloc(unsigned long, unsigned long), JavaScriptCore Note that the addition of a single, 1px solid colored border has caused a massive increase in the time spent during rendering. Very, (very!), roughly, the time spent rendering for the exact same sequence of steps (a mouseover of elements that change due to a css :hover, approximately): No border: 172.4ms W/ border: 726.0ms This is just from the top 5 samples, and including a single 1px solid #f00 border causes a 4.21x, or 421% jump in the time spent rendering. On my machine, this makes the mouseover :hover events noticeably sluggish and lag way behind when the mouse enters or leaves an elements area. Since my machine is relatively old at this point, faster machines might seem more responsive and it might not be noticeable, or AS noticeable, so keep that in mind when verifying this bug. Probably best to verify using Shark using the steps outlined above. While working on this, trying to find the problem, I did notice that "less stuff" on the page made the problem less obvious, which is why I included 'the whole nine yards', so to speak. Again, see the attached tarball for the web pages that recreate this problem. This was cut down from the full page, and not knowing exactly what's contributing or causing the problem I've left an awful lot of clearly unnecessary 'cruft' in there, the javascript stuff isn't even used. However, it does manage to illustrate the problem by changing a single line only: adding a 1px solid #f00 border. It's almost as if adding the single 1px border causes a fast-path/slow-path test to favor the slow path for some reason? I don't know the code base one bit (so take this for what it's worth, nothing), but I took a stab at trying to identify the cause of the problem. I found one difference in the stack traces that stood out when I stared and compared. The 'fast' version had a call graph like this (top calls bottom): 2 65.6 ms WebCore::GraphicsContext::drawTiledImage(WebCore::Image*, WebCore::IntRect const&, WebCore::IntPoint const&, WebCore::IntSize const&, WebCore::CompositeOperator) 1 65.6 ms WebCore::Image::drawTiled(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::FloatPoint const&, WebCore::FloatSize const&, WebCore::CompositeOperator) 0 99.6 ms WebCore::GeneratedImage::draw(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::FloatRect const&, WebCore::CompositeOperator) The 'slow' version had this: 3 459.7 ms WebCore::GraphicsContext::drawTiledImage(WebCore::Image*, WebCore::IntRect const&, WebCore::IntPoint const&, WebCore::IntSize const&, WebCore::CompositeOperator) 2 459.7 ms WebCore::Image::drawTiled(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::FloatPoint const&, WebCore::FloatSize const&, WebCore::CompositeOperator) 1 459.7 ms WebCore::GeneratedImage::drawPattern(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::AffineTransform const&, WebCore::FloatPoint const&, WebCore::CompositeOperator, WebCore::FloatRect const&) 0 459.7 ms WebCore::Image::drawPattern(WebCore::GraphicsContext*, WebCore::FloatRect const&, WebCore::AffineTransform const&, WebCore::FloatPoint const&, WebCore::CompositeOperator, WebCore::FloatRect const&) Or, in other words, WebCore::Image::drawTiled seemed to be calling WebCore::GeneratedImage::draw() on the fast version, but WebCore::GeneratedImage::drawPattern() on the slow version. Looking at the source file in WebCore/platform/graphics/Image.cpp, inside the function void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, CompositeOperator op), it has a conditional test: // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect)) { /* essentially calls draw(), then returns */ } /* else, essentially calls drawPattern(), which seems to be much more expensive from looking at the code for it */ This seems to fit closely to the stack traces. Now, I don't know squat about the code base, but it seems sort of odd that a 1 px border is causing such a huge performance impact. Assuming that this isn't some kind of 'off by one'ish type of bug, the performance impact is of such a magnitude (and at least anecdotally seems to get better/worse modulo the page complexity) that it would seem to be worthwhile to special case out certain conditions. I would almost think that a border would be essentially identical to the non-border case, just offset by the border amount. Maybe that's the problem with the condition test above, one rect is offset to include the border, the other is not, causing a 'contains()' test to fail? The attached tarball includes the shark sessions, which are quite large unfortunately.
Attachments
Example html/css files that demonstrate problem (71.38 KB, application/octet-stream)
2008-09-18 17:31 PDT, John Engelhart
no flags
Shark sessions from my machine for topbar.html and topbar_slow.html (deleted)
2008-09-18 17:36 PDT, John Engelhart
no flags
John Engelhart
Comment 1 2008-09-18 17:31:25 PDT
Created attachment 23544 [details] Example html/css files that demonstrate problem The two files of interest are: topbar.html topbar_slow.html These files differ by a single line only, at the top, inside the <style> section. The _slow version as the border: 1px solid #f00 attribute removed.
John Engelhart
Comment 2 2008-09-18 17:36:50 PDT
Created attachment 23547 [details] Shark sessions from my machine for topbar.html and topbar_slow.html These are the shark sample sessions from my machine for the two .html files. The following steps were used to create the samples: Move to left RegexKit Download Now button. Move to right RegexKitLite Download Now button. Move up to Sourceforge.net text Move right to Contact text. Move up to search field. Move right to left over "Documentation Download Forums Tickets" Specifically: The page was reloaded. The above sequence was performed once to 'warm up' any caches. Sampling was started (option-esc). The steps were performed again. Sampling was stopped (option-esc).
Simon Fraser (smfr)
Comment 3 2010-12-21 12:41:47 PST
When you add a border, we have to tile the background under the border area. However, in many cases we could avoid this (bug 37241).
Note You need to log in before you can comment on or make changes to this bug.