Bug 215989 - REGRESSION(r262366): MotionMark1.1 | macOS | Some devices | 1-3% overall regression
Summary: REGRESSION(r262366): MotionMark1.1 | macOS | Some devices | 1-3% overall regr...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Dean Jackson
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-08-30 12:37 PDT by Dean Jackson
Modified: 2020-09-01 18:47 PDT (History)
10 users (show)

See Also:


Attachments
Patch (17.46 KB, patch)
2020-08-30 12:48 PDT, Dean Jackson
darin: review+
Details | Formatted Diff | Diff
Trace (before patch) (26.30 MB, application/zip)
2020-08-31 17:51 PDT, Dean Jackson
no flags Details
Instruments screenshot (483.40 KB, image/png)
2020-08-31 17:57 PDT, Dean Jackson
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dean Jackson 2020-08-30 12:37:35 PDT
REGRESSION(r262366): MotionMark1.1 | macOS | Some devices | 1-3% overall regression
Comment 1 Dean Jackson 2020-08-30 12:39:06 PDT
<rdar://problem/66845937>
Comment 2 Dean Jackson 2020-08-30 12:48:07 PDT
Created attachment 407571 [details]
Patch
Comment 3 Darin Adler 2020-08-30 13:08:17 PDT
Comment on attachment 407571 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=407571&action=review

> Source/WebCore/dom/Document.cpp:8639
> +    if (m_canvasesNeedingDisplayPreparation.isEmpty())
> +        return;

This isn’t needed. The code below already efficiently does nothing in this case. Making an empty vector, an empty for loop, and an empty clear function are all efficient.

> Source/WebCore/html/CanvasBase.cpp:140
> +    if (m_observers.isEmpty())
> +        return;

This isn’t needed. The code below already efficiently does nothing in this case. Making an empty vector and an empty for loop are efficient.

> Source/WebCore/html/CanvasBase.cpp:149
> +    if (m_observers.isEmpty())
> +        return;

This isn’t needed. The code below already efficiently does nothing in this case. Making an empty vector and an empty for loop are efficient.

> Source/WebCore/html/CanvasBase.cpp:162
> +    if (m_observers.isEmpty()) {
> +#if ASSERT_ENABLED
> +        m_didNotifyObserversCanvasDestroyed = true;
> +#endif
> +        return;
> +    }

This isn’t needed. The code below already efficiently does nothing in this case. Making an empty vector, an empty for loop, and an empty clear function are all efficient.

> Source/WebCore/html/HTMLCanvasElement.cpp:1054
> +    if (needsPreparationForDisplay()) {
> +        if (insertionType.connectedToDocument) {

I suggest && rather than a nested if statement.

> Source/WebCore/html/HTMLCanvasElement.cpp:1055
> +            auto& doc = parentOfInsertedTree.document();

Please use document, not doc.

> Source/WebCore/html/HTMLCanvasElement.cpp:1060
> +            if (is<WebGLRenderingContextBase>(m_context.get())) {
> +                if (downcast<WebGLRenderingContextBase>(m_context.get())->compositingResultsNeedUpdating())

Three thoughts:

1) Instead of "(m_context.get())->" you can use "(*m_context)." and this applies here and other places in the file.
2) Instead of nested if you can use &&, which I think is clearer, but it ends up being a long line because of repeating the WebGLRenderingContextBase class name twice.
3) Could make compositingResultsNeedUpdating a virtual function and avoid the type check entirely.

> Source/WebCore/html/HTMLCanvasElement.cpp:1072
> +    if (needsPreparationForDisplay()) {
> +        if (removalType.disconnectedFromDocument) {

I suggest && rather than a nested if statement.

> Source/WebCore/html/HTMLCanvasElement.cpp:1084
> +    if (!m_context)
> +        return false;

This isn’t needed. The code below already efficiently returns false when m_context is null.
Comment 4 Dean Jackson 2020-08-31 11:29:39 PDT
I forgot to say that I measured and this does recover the 1-3% slowdown.
Comment 5 Dean Jackson 2020-08-31 11:33:10 PDT
Thanks Darin! I'll make all the suggested changes.
(and update the changelog to note that I measured performance).

As for the empty functions, I obviously didn't look at the implementation, but I assumed that the copyToVector(empty-HashSet) would have to actually create a Vector in order for the loop to know it was empty. I assume that's so insignificant it doesn't matter?
Comment 6 Darin Adler 2020-08-31 11:57:46 PDT
(In reply to Dean Jackson from comment #5)
> I assumed that the copyToVector(empty-HashSet) would have to actually
> create a Vector in order for the loop to know it was empty. I assume that's
> so insignificant it doesn't matter?

Yes, it has to create a vector.

But a 0-sized vector is all on the stack (not the heap) and creating one should not involve any memory allocation and should not be all that different from checking the set for emptiness.
Comment 7 Dean Jackson 2020-08-31 17:51:22 PDT
Created attachment 407640 [details]
Trace (before patch)

If I look at the attached trace (which is without this patch), and search for CanvasBase::notifyObserversCanvasChanged I see copyToVector allocating a buffer which calls into bmalloc.
Comment 8 Dean Jackson 2020-08-31 17:57:09 PDT
Created attachment 407642 [details]
Instruments screenshot
Comment 9 Darin Adler 2020-08-31 17:57:54 PDT
OK, but that’s not an empty set.
Comment 10 Darin Adler 2020-08-31 17:59:31 PDT
Is this called with non-empty sets too during the test?
Comment 11 Darin Adler 2020-08-31 18:00:06 PDT
Do we really need to copy the vector every time? Can the canvasChanged functions mutate the observers set?
Comment 12 Dean Jackson 2020-08-31 18:33:40 PDT
Oh. I'm a moron!

The whole point of this patch was to make that HashSet empty in this case. It isn't empty before the fix.
Comment 13 Dean Jackson 2020-08-31 18:33:53 PDT
Ha. Darin guessed it as I worked it out.
Comment 14 Dean Jackson 2020-08-31 18:38:20 PDT
> Do we really need to copy the vector every time? Can the canvasChanged functions mutate the observers set?

The answer is yes, they technically can.

I thought about different ways to avoid this. Right now the vectors don't change because I control the calls inside both the canvas observer updates and the after rendering updates, but someone could something bad if they didn't know.

Maybe I could pass const CanvasBases around, but the called functions still need to be non-const.
Comment 15 Dean Jackson 2020-08-31 18:41:03 PDT
I can't pass const CanvasBase, because they ultimately need to call a non-const.

But yeah, a canvasChanged call should never mutate the set of observers on itself.
Comment 16 Dean Jackson 2020-08-31 18:43:57 PDT
"notifyObserversCanvasDestroyed" might though, since the notification might do something to remove itself as an observer (even though it's already being deleted).
Comment 17 Dean Jackson 2020-08-31 18:59:40 PDT
> 3) Could make compositingResultsNeedUpdating a virtual function and avoid the type check entirely.

I made compositingResultsNeedUpdating, prepareForDisplay and needsPreparationForDisplay all virtuals, which cleans up the code significantly.
Comment 18 Dean Jackson 2020-09-01 18:47:12 PDT
Committed r266443: <https://trac.webkit.org/changeset/266443>