Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resizer performance #5191

Closed
Reinmar opened this issue Aug 20, 2019 · 4 comments · Fixed by ckeditor/ckeditor5-image#330 or ckeditor/ckeditor5-widget#108
Closed

Resizer performance #5191

Reinmar opened this issue Aug 20, 2019 · 4 comments · Fixed by ckeditor/ckeditor5-image#330 or ckeditor/ckeditor5-widget#108
Assignees
Labels
package:image type:improvement This issue reports a possible enhancement of an existing feature.

Comments

@Reinmar
Copy link
Member

Reinmar commented Aug 20, 2019

In general, the performance of the resizer is not great. We do simple debouncing, but it does not actually change anything. I think it was added there prematurely.

This is no debouncing of mousemove:

image

And this is the current master:

image

If you look at the frames, you can actually see better results without debouncing. But my guess is – this might've been due to me dragging a bit differently in both cases (different speed). In any case, as long as we get only 5fps, debouncing seems completely useless, if not harmful. I'm not sure about this, but my guess will be that the browser throttles mousemove itself anyway – it'd be nice to confirm that.

To really understand the performance we need to dig into the timeline. To see why we get such long frames. Let's see how long it takes to handle one mousemove.

This is a single task:

image

Let's analyse it:

  1. The first peak is updateSize() call so the actual mousemove listener. It looks quite ok:

    image

    We can see that there two costly actions – changing the size of the image itself. And then of the wrapper. Unfortunately, between those actions we need to take the rect to learn how the image grew. This is because we want to position the resize wrapper pixel perfect, regardless of the styling. We could perhaps save some time optimising how the resize wrapper is positioned, but the truth is that this isn't crucial in this picture because the entire updateSize() function takes just a fraction of the entire task (2ms from 16ms).

  2. Then we get to this part:

    image

    And this is where we've got problems. This entire piece of code should not be executed when the user is resizing an image.

    • There's no need to do anything with the fake selection for sure. I think we may be trying to fix it because mousedown cause the fake selection container to lose focus and the browser to try to move the selection somewhere else. We should find ways to prevent any interaction with the selection on mousedown and mousemove here.
    • Then we try to reposition the toolbar. We can simply hide the toolbar during resizing. That will simply make the problem go away.
    • Finally, there's redraw() from the resizer. Which... I totally don't get. Since updateSize() resized the wrapper already, why do we need redraw()? It's super costly, BTW. I guess it does something more than updateSize() but then it should be clarified and cleaned up what does what for what reason.
@Reinmar
Copy link
Member Author

Reinmar commented Aug 20, 2019

BTW, this looks really bad too:

		const redrawResizers = throttle( () => {
			for ( const resizer of this.resizers ) {
				resizer.redraw();
			}
		}, THROTTLE_THRESHOLD );

		// Redrawing on any change of the UI of the editor (including content changes).
		this.editor.ui.on( 'update', redrawResizers );

		// Resizers need to be redrawn upon window resize, because new window might shrink resize host.
		this._observer.listenTo( global.window, 'resize', redrawResizers );

We actually redraw all resizers whenever anything changes. And things do change frequently during resizing. But... only one resizer is visible, so there's no point in redrawing all of them.

@Reinmar
Copy link
Member Author

Reinmar commented Aug 20, 2019

I've dug a bit more and redraw() does one more thing compared to updateSize() – it positions the wrapper inside the <figure>.

I think it'd be best to do this:

  • remove wrapper updates from updateSize(),
  • call redraw() of the visible resizer on editor.ui#update,
  • make redraw() calculate it all – the width/height and top/left.

@Reinmar
Copy link
Member Author

Reinmar commented Aug 20, 2019

OK, so I quickly did the things I mentioned above and I got far better results:

image

Frames length shrank from ~150-200ms to 60-80ms. There's still more that we can do (no unnecessary throttling, fake selection fix, toolbar positioning fix) but it's a good first step.

You can check the code on https://github.com/ckeditor/ckeditor5-widget/pull/new/t/ckeditor5-image/310

mlewand referenced this issue in ckeditor/ckeditor5-engine Sep 6, 2019
…for the same fake selection.

Constantly changing the selection poses a severe bottleneck for browser rendering, observed in the image resize feature (see ckeditor/ckeditor5-image#310).
@mlewand
Copy link
Contributor

mlewand commented Sep 6, 2019

During the implementation it turned out that also #1791 is having a severe impact on this.

In a nutshell dragging pressed mouse over editable caused a lot of calls to setting fake selection (though the selection was the same, engine performed a real selection change for each call). This resulted with prolonged rendering frames.

@mlewand mlewand transferred this issue from ckeditor/ckeditor5-image Oct 9, 2019
@mlewand mlewand added this to the iteration 27 milestone Oct 9, 2019
@mlewand mlewand added status:confirmed type:improvement This issue reports a possible enhancement of an existing feature. package:image labels Oct 9, 2019
Reinmar added a commit to ckeditor/ckeditor5-widget that referenced this issue Oct 16, 2019
Reinmar added a commit to ckeditor/ckeditor5-image that referenced this issue Oct 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:image type:improvement This issue reports a possible enhancement of an existing feature.
Projects
None yet
2 participants