WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.32k stars 4.12k forks source link

Big performance degradation in WP 6.6 in editor #64732

Open wpsoul opened 3 weeks ago

wpsoul commented 3 weeks ago

Description

I see huge performance drop in 6.6 version in editor. After React profiler, I see that there is Shuffle process on all spikes. Also, very slow RichText component. After downgrade to 6.5.5, I don't see this. Why I have Shuffle function on third party blocks?

Step-by-step reproduction instructions

  1. Create page with many columns, rows, inner blocks.
  2. If you have fast device, you need to Slow down CPU in Web developer panel
  3. Start editing text, after 20-30 seconds you will see huge freezes

Screenshots, screen recording, code snippet

https://monosnap.com/file/WmQFBqsQlCM2TBD3V5nEqIe7dkepH5

Environment info

No response

Please confirm that you have searched existing issues in the repo.

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Mamaduka commented 3 weeks ago

@wpsoul, could you share the blocks you're using for testing? You can easily copy the test content using the "Copy all blocks" action from the "Options" dropdown.

The selected block in the screencast is "Heading Advanced." Are you testing typing in this block? Do you get the same results with core blocks?

The project has a tool to monitor editor performance, and I don't see any spikes there - https://www.codevitals.run/project/gutenberg/

wpsoul commented 3 weeks ago

@Mamaduka I got a performance drop with all blocks, including the core block, and also if I disable all plugins.

You will see this with special conditions.

You need to have many blocks on page with many inner blocks. At least 10 sections. More blocks - slower it will be.

Second - you will see degradation not on first loading, but when you start to work. After 30-60 seconds. Highly likely related to RichText, because when I type more and more text, I see more and more delay in rendering.

Third - if you have good laptop with good CPU, you will not see a problem (but in the same time you will see huge CPU usage if you open Monitor). So, to test this on good computer, you need to enable CPU slowdown in Performance tab in web developer tools.

Anyway, what Shuffle does on all blocks? https://github.com/WordPress/gutenberg/blob/b194a803ab4061adc27a3c037f0099d3a3c1152e/packages/block-editor/src/components/block-toolbar/shuffle.js#L25 I think it must work only on specific blocks, not on all.

This bug can be related to some reported problems with patterns, because Shuffle also related to patterns

Mamaduka commented 3 weeks ago

Thanks for the update, @wpsoul!

I would greatly appreciate it if you could share the example test data you're using. Otherwise, I can come up with something.

wpsoul commented 3 weeks ago

@Mamaduka data can be any, I created post for you so you can copy https://gist.github.com/wpsoul/22af7e513064213c8595067589656bf7

wpsoul commented 3 weeks ago

@Mamaduka I think I got good progress in location. When I measure performance in Web inspector, I found that Shuffle and experimentalReusableBlocksSelect degrades performance. Shuffle degrades initial loading, but experimentalReusableBlocksSelect degrades performance when you continue to work on page

In wp-includes/js/dist/editor.js, when I remove next line

https://share.cleanshot.com/4Mv380HN

everything is back to normal, much much faster. So, somewhere in this function there is big memory leak, also it triggers __unstableMarkListeningStores, this is why it's degrading when you continue to work, I guess because it's continue to store leak.

Mamaduka commented 3 weeks ago

Thanks, @wpsoul!

Yes, that looks similar to @kevin940726's finding - https://github.com/WordPress/gutenberg/issues/64219#issuecomment-2289429459.

I created a PR to prevent unnecessary __experimentalGetAllowedPatterns calls in the Suffle component (#64736). However, this is only a partial solution.

wpsoul commented 3 weeks ago

@Mamaduka Yes, it should solve related to Shuffle. But what about __experimentalReusableBlocksSelect ? I think it's even worse. See how slow with it

https://monosnap.com/file/DnbZfBnlimmgtrizDzzQqHokr1IL2s

and this is when I removed __experimentalReusableBlocksSelect

https://monosnap.com/file/v9jrJQf82YIWIfB6i9t1QR0fCln3dH

it doesn't matter how long I am working in editor, it doesn't degrade performance if I remove __experimentalReusableBlocksSelect

Mamaduka commented 3 weeks ago

@wpsoul, as I mentioned, it's only a partial solution.

I will look separately at __experimentalGetAllowedPatterns and __experimentalReusableBlocksSelect optimizations. Unfortunately, we can't just remove the setting.

wpsoul commented 3 weeks ago

@Mamaduka

Just for testing, I added console log in __experimentalReusableBlocksSelect to see how much time it's called. And OMG, I understand why it degrades performance. 17000 calls on big pages. Any action calls another 300 times.

https://monosnap.com/file/yCjljGzqxcojVplqWAr67AAp6j1cgO

I tried also on totally clean page, no blocks at all. More than 300 calls. Why? It's also recalling them on any action. And more elements on page, more recalls on any action. Looks like it takes all items on page and calls reusable calculation for each of them

https://monosnap.com/file/pCbnn75hgarldkmpyICcGqXHGDSzil

this is definitely very critical problem.

Also, for me, it's still not clear why we need to rerender all blocks on Intersection observer that I mentioned

https://github.com/WordPress/gutenberg/issues/56664

I understand if it's rendering blocks first time when blocks are in view. But it's not, it's just rerender everything on top of already rendered state.

wpsoul commented 3 weeks ago

@Mamaduka Until it's fixed, is it possible somehow to disable this in block? I tried to set supports:{reusable: false} but maybe it's required to add this to all blocks?

Mamaduka commented 3 weeks ago

The reusable: false controls only if a block becomes reusable.

Unfortunately, I don't think there's a way to temporarily deactivate it. We'll have to wait for a proper fix.

briangrider commented 2 weeks ago

Thank you for your persistence with this @wpsoul and @Mamaduka. This seems like a really big issue and echos a lot of complaints I've seen in the community about slow downs related to reusable patterns. @Mamaduka, this seems like a pretty critical issue, right? I was trying to trace why this is causing up to 17000+ re-renders with big pages (as reported by @wpsoul) and I couldn't pinpoint it in the code. Do you know what files/lines this call is happening at within blocks themselves? I know @wpsoul said commenting out line 300 in /packages/editor/src/components/provider/use-block-editor-settings.js fixes the problem, but tracing backwards from there in the codebase didn't reveal anything to me about where the actual re-renders are happening.

But if it's related to the number of blocks on the page, that seems like an architectural flaw in Gutenberg. Why should blocks be making any calls related to patterns and reusable patterns. And if it's not within the blocks themselves and it's because the EditorProvider component is re-rendering and forcing all editor children to re-render, including all blocks, that's just as bad, right? Thanks to everyone involved in working to get this fixed - hoping there's a way forward soon as I've been seeing a number of people say the block editor and site editor are completely unusable for them now.

kevin940726 commented 2 weeks ago

Could you still reproduce it after #64736? That seems to fix the problems for most cases for me. However, I agree with @Mamaduka that __experimentalGetAllowedPatterns should still be optimized. Maybe there are still some unhappy paths for that to behave slowly.

Regarding unnecessary re-renders, in general, I think it's okay to re-render many times, as long as they're not the bottlenecks. Note that re-rendering does not mean committing to DOM, which is often the most expensive operation. React makes sure to do the least amount of work when doing DOM operations.

If this issue can no longer be reproduced, what do you all think about closing this and moving the discussion to #64219? We can gather more feedback if we move the discussions to one place. (c.c. @ellatrix and @youknowriad for visibility on #64219 again too)

wpsoul commented 2 weeks ago

@briangrider @Mamaduka to summarize, file wp-includes/js/dist/editor.js

this line is calling __experimentalReusableBlocksSelect for blocks

https://share.cleanshot.com/BSDb3HCL

I believe that every block makes this call and what is even more critical, it's not only called but also executed All reusable templates.

So, if you have 30 blocks on page and 30 reusable templates, 900 blocks will be rendered in total and if reusable template have other blocks, they will also make __experimentalReusableBlocksSelect request (each block). I am not sure if it's always or under specific conditions. I think it's critical and I checked several Gutenberg addons and all of them have latest reports related to significant slow down of editor on long pages.

How to reproduce: create near 5-10 synced patterns. Add some blocks in each. Create new page, add, for example 10 blocks. add console.log to __experimentalReusableBlocksSelect. I believe, it should be called only once on page maximum, but you will see a lot of calls. And each action will add few more

ellatrix commented 2 weeks ago

Would you be able to test https://github.com/WordPress/gutenberg/pull/64871?

wpsoul commented 2 weeks ago

@ellatrix this fixed Shuffle, but didn't fix reusable templates. I made more tests and now even more strange things.

First - I disabled all plugins, tried different themes, I think it doesn't depend on theme.

Now, I added console.log in your pr in file build/editor/index.min.js

here https://share.cleanshot.com/YyQlCyW6

it's __experimentalReusableBlocksSelect function, I want to check how much time it's called on page

Now, I add new page. I don't add any blocks, and I see 384 calls of console.log I believe it's related to feature that shows me popup with Pattern selection for page. But the same problem I see in any theme that doesn't have patterns for pages.

Interesting that when I put any block on page, I see that it's calling only few times. I guess, because pattern popup is disabled

Also, paragraph is fine. But see what happens if I use any action on block with inner blocks. It's calling again __experimentalReusableBlocksSelect many times. More bigger page, more calls. And things become slow down in geometrical progression if you put many nested elements - group inside group inside group, etc. And it doesn't matter where you add action, calls depend on how much nested element you have on page in general.

https://monosnap.com/file/pYh0AC2uLIeIihJjXzjOOVcUjL7wGf

To be true, I don't understand why reusable templates are retrieved in blocks. They must be retrieved on page and only once.

Mamaduka commented 2 weeks ago

@wpsoul, the #64871 is a different PR.

While __experimentalReusableBlocksSelect might be called a few hundred times during the editor's lifecycle, it's not an actual bottleneck; other selectors that consume it are.

wpsoul commented 2 weeks ago

@Mamaduka yes, it's not function itself, something is rerendering whole editor multiple times when we add any changes in nested blocks

Profiler shows me also location in __unstableMarkListeningStores

wpsoul commented 2 weeks ago

@Mamaduka If this will help, here is full stack of slow process CleanShot 2024-08-28 at 22 19 10@2x

Mamaduka commented 2 weeks ago

@wpsoul, are you testing using the latest Gutenberg trunk?

wpsoul commented 2 weeks ago

@Mamaduka Yes, I tested with latest 19.1 version. I made another simple test. No plugins, except Gutenberg, 2024 theme, 50 empty Group blocks. Added console.log to __experimentalReusableBlocksSelect

See this madness, 6000 calls on loading, another + 1000 calls on any action. No, it's not correct to have such amount of rerenderings. I even don't understand why we need to rerender this function. Problem is not in function, but in fact that editor rerenders everything for each block hundreds or thousands of times

https://monosnap.com/file/gSBCJtm9XTk4oTKnq5O69e87PAt285

Important fact. This doesn't happen if I have direct blocks on page without inner blocks. For example, if I add only headings, it doesn't matter how much headings I have (test on > 500), they will call function only 6 times.

https://monosnap.com/file/09FzstPEcnCYLXJIYIMIAOliFdP4wP

wpsoul commented 2 weeks ago

@ellatrix @Mamaduka I think I am close to find memory leak point. See, in my previous test I used 500 heading blocks. And they are fine. 7 console log calls. Now, I add one empty Row block. And immediately, I got + 116 calls on loading

https://share.cleanshot.com/TMgnDySB

wpsoul commented 2 weeks ago

@ellatrix @Mamaduka I think I found location. It's very strange, but I think it's renderAppenderButton

if you add InnerBlocks.ButtonBlockAppender or InnerBlocks.DefaultBlockAppender will slow down everything significantly

ellatrix commented 2 weeks ago

@wpsoul Does this fix your problem? #64902

wpsoul commented 2 weeks ago

@ellatrix nope, still it makes many rerenders for InnerBlocks.ButtonBlockAppender. Number was reduced a bit. On page where I had 6000 calls, I have now 5500. But it's still too much (I have 50 blocks there) + on each focus of block, I still have near +200 calls