[Webkit-unassigned] [Bug 20929] New: Major performance degradation when border is added to element

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Thu Sep 18 17:25:58 PDT 2008


https://bugs.webkit.org/show_bug.cgi?id=20929

           Summary: Major performance degradation when border is added to
                    element
           Product: WebKit
           Version: 528+ (Nightly build)
          Platform: Macintosh
        OS/Version: Mac OS X 10.5
            Status: UNCONFIRMED
          Severity: Normal
          Priority: P2
         Component: New Bugs
        AssignedTo: webkit-unassigned at lists.webkit.org
        ReportedBy: john.engelhart at gmail.com


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.


-- 
Configure bugmail: https://bugs.webkit.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.



More information about the webkit-unassigned mailing list