Closed Bug 1659773 Opened 4 years ago Closed 2 years ago

Web console recreates React elements for all messages in each render

Categories

(DevTools :: Console, defect, P2)

defect

Tracking

(Not tracked)

RESOLVED DUPLICATE of bug 1753177

People

(Reporter: bradwerth, Unassigned)

References

Details

Attachments

(2 files, 7 obsolete files)

React rendering compares the elements (objects) submitted to it to determine if DOM changes are necessary. In order to minimize the work done by React, it is most efficient for an unchanged console message to be represented by the same object on subsequent renders. This cuts down on calls to React createElement, which shows up in performance profiles as a significant factor when many messages are generated.

See Also: → 1308216

This patch trades memory for efficiency. This makes the ConsoleOutput store
the React elements it sent for rendering on the last render call. On the next
render call, it then re-sends any existing objects it is storing, creates the
new ones, and then deletes any forgotten objects.

Blocks: 1411937

I think this is showing improvement in most console tests. There is a regression in console.objectexpanded.close.DAMP test, but the results are within the margin of error -- analysis marks this one as medium confidence. I'll seek review.

(In reply to Brad Werth [:bradwerth] from comment #2)

I think this is showing improvement in most console tests. There is a regression in console.objectexpanded.close.DAMP test, but the results are within the margin of error -- analysis marks this one as medium confidence. I'll seek review.

You should toggle "hide uncertain results", and then it only shows improvements.
But this is surprising that we don't see more wins on the console. It might be worth pushing your base revision to DAMP to get more confidences. When using "last x days of m-c" you get mixed results if anything regressed/improved in these last x days.

(In reply to Alexandre Poirot [:ochameau] from comment #3)

You should toggle "hide uncertain results", and then it only shows improvements.

Yes, I had seen that, but I didn't want to elide a possible regression. Another low-confidence result is the improvement to complicated.webconsole.reload.DAMP. If reload avoids re-creating the ConsoleOutput object (does it?) then I would expect this test to show the improvement that it apparently does.

Once created, these objects are not modified further. This ensures that React
can skip over some equality checks.

Attachment #9170738 - Attachment description: Bug 1659773: Make the web console reuse message elements in each React render. → Bug 1659773 Part 1: Make the web console reuse message elements in each React render.
Attachment #9170816 - Attachment is obsolete: true

These patches don't yet work, but do represent the only promising approach I've found for reducing the number of calls to React.createElement.

Attached file consoleslow1.html

The patches work as intended now. The speedup is more modest than I anticipated. Here's some times from different browser builds (reload with the console visible) to compare on this test file:

Chrome: 0.1s
Firefox Release (unpatched): 0.5s
Firefox Debug (unpatched): 80s - 100s
Firefox Debug (patched): 65s - 75s

So perhaps this is giving us a 25% reduction in runtime. I would project this would reduce the Release time to 0.4s. I'll run a new profile and see what the new opportunities are for improvement. If they are sufficiently related to this work, I'll keep adding to the patches in this bug. If not, I'll file a new bug.

The new profile https://share.firefox.dev/362rRZW confirms that the patch is working as intended. ConsoleOutput.render is now 2.2% of the runtime section of interest. There aren't other obvious large blocks to pursue. Here's some relevant items:

Frame.render: 6.4%
Message.render: 7.1%
MessageContainer.render: 7.8%
ConsoleOutput.render: 2.2%

The rendering methods combined are now about 23% of the runtime -- comparable to the gains already achieved by the reduction to time spent in ConsoleOutput.render. Even if we could somehow reduce render to 0s, we'd only shorten runtime by that much. There aren't obvious bad call patterns here.

MessageContainer.shouldComponentUpdate: 4.1%

There might be gains to be made here. The shouldComponentUpdate method should always be fast in order to be useful. Otherwise, just always update!

Document.createElementNS: 2.4%

This is cost of modifying the DOM.

PresShell::FlushPendingNotifications: 5.5%

This is our layout cost. Seems reasonable, though we might be able to reduce it by calculating the height of each console item (after initial layout). This is evidence that virtualized scrolling is not relevant to this slow case.

ActivityStreamProvider.getTopFrecentSites: 2.8%

I have no idea what this is doing here. Even if it's just a coincidence that this scheduled operation overlapped with my profile, it's still true that this operation is much slower than it should be.

messages reducer: 4.7%

This is split between time in addMessages and cloneState. There may be opportunities here.

Okay, that's not everything, but it's all the notable chunks that we might be able to affect. I'll see if I can add something in this patch stack to reduce time in MessageContainer.shouldComponentUpdate and in the messages reducer. I don't know what to do about ActivityStreamProvider.getTopFrecentSites.

The call to MessageContainer.shouldComponentUpdate function is expensive due
to comparison of native properties. Since MessageContainers are only ever
regenerated when they change, they never need to update and the
shouldComponentUpdate function is totally unnecessary. In React, that is
handled by defining MessageContainer as a PureComponent, which has a fast
default shouldComponentUpdate call.

Depends on D90758

That's as far as I can get, performance-wise. I'm going to add another part to address test failures, then seek review.

Attachment #9170738 - Attachment description: Bug 1659773 Part 1: Make the web console reuse message elements in each React render. → Bug 1659773 Part 1: [devtools] Make the web console reuse message elements in each React render.
Attachment #9176675 - Attachment description: Bug 1659773 Part 2: Give the message store callbacks to use as visibleMessages are changed. → Bug 1659773 Part 2: [devtools] Give the message store and ui store callbacks to maniuplate messageElements.
Attachment #9176676 - Attachment description: Bug 1659773 Part 3: Make the message store call the callbacks as messageIds enter and leave visibleMessages. → Bug 1659773 Part 3: [devtools] Make the message store and ui store manipulate messageElements.
Attachment #9177534 - Attachment description: Bug 1659773 Part 4: Make MessageContainer into a PureComponent. → Bug 1659773 Part 4: [devtools] Make MessageContainer into a PureComponent.
Attachment #9176675 - Attachment description: Bug 1659773 Part 2: [devtools] Give the message store and ui store callbacks to maniuplate messageElements. → Bug 1659773 Part 2: [devtools] Give the message store and ui store callbacks to manipulate messageElements.

Some tests fail intermittently due to messages being sent after the transport
has been closed. Those messages are safely ignorable.

Depends on D91230

Attachment #9179396 - Attachment is obsolete: true

I'm going to add a blocker bug to this bug that adds a console logging DAMP test, so we have comparable performance before we try to land this.

(In reply to Brad Werth [:bradwerth] from comment #18)

I'm going to add a blocker bug to this bug that adds a console logging DAMP test, so we have comparable performance before we try to land this.

It looks like Bug 1593728 will serve as that blocker.

Depends on: 1593728

To try out alternative approaches, I've built a toy version of our React-based console. The goal is to find a way to make the addition of one message fast even after many thousands of messages are already being tracked.

Comment on attachment 9184943 [details]
WIP toy version of our React-based console design

http://www.bradleyjwerth.com/console-toy/
Attachment #9178819 - Attachment is obsolete: true
Attachment #9177534 - Attachment is obsolete: true
Attachment #9176676 - Attachment is obsolete: true
Attachment #9176675 - Attachment is obsolete: true
Attachment #9170738 - Attachment is obsolete: true

I'm not working on this anymore.

Assignee: bwerth → nobody

This will probably be fixed by bug 1753177 too.

Status: NEW → RESOLVED
Closed: 2 years ago
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: