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

Very slow handling of SWT messages when millions of messages queued #74

Closed
jukzi opened this issue Apr 28, 2022 · 10 comments
Closed

Very slow handling of SWT messages when millions of messages queued #74

jukzi opened this issue Apr 28, 2022 · 10 comments

Comments

@jukzi
Copy link
Contributor

jukzi commented Apr 28, 2022

During eclipse-pde/eclipse.pde#53 is see terrible hotspots in

  • org.eclipse.swt.widgets.Synchronizer.addLast ()
  • org.eclipse.swt.graphics.Device.isDisposed ()
  • org.eclipse.swt.widgets.Synchronizer.asyncExec ()
  • Display.findDisplay ()

while the amount of messages in org.eclipse.swt.widgets.Synchronizer.addLast(RunnableLock) was > 1million.

All those functions show "synchronized" blocks, which could be probably avoided with the usage of appropriate classes of java.util.concurrent.

Sampling of SWT Thread:
image

And asyncExec() seen in a sampling of a background thread:
image

@merks
Copy link
Contributor

merks commented Apr 28, 2022

Good finding! My sense is that org.eclipse.pde.internal.ui.editor.context.InputContextManager.resourceChanged(IResourceChangeEvent) should just build sets of added and removed resources and then do a single asyncExec in which it calls org.eclipse.pde.internal.ui.editor.context.InputContextManager.structureChanged(IFile, boolean) for each of those added/removed things. Then likely all the other methods will not be a significant performance concern anymore.

@vogella
Copy link
Contributor

vogella commented Apr 28, 2022

PR request are welcome to replace the sync blocks, I assume that would benefit all parts of SWT.

@iloveeclipse
Copy link
Member

Most parts of SWT don't use any sync blocks, because SWT is single threaded. There are only few that contact with the "outer" world :-)

@merks
Copy link
Contributor

merks commented Apr 28, 2022

@vogella

Of course improvements are always welcome. :-) But no amount of improvement will make doing asyncExec per changed resource when processing a resource delta as reasonable design! :-P That's definitely just bad design and should be fixed.

@jukzi Are you planning on fixing the "too many asyncExecs" problem?"

@vogella
Copy link
Contributor

vogella commented Apr 28, 2022

@vogella

Of course improvements are always welcome. :-) But no amount of improvement will make doing asyncExec per changed resource when processing a resource delta as reasonable design! :-P That's definitely just bad design and should be fixed.

Of course but this discovery is still good as it may speed up SWT in general.

@jukzi
Copy link
Contributor Author

jukzi commented Apr 28, 2022

@jukzi Are you planning on fixing the "too many asyncExecs" problem?"

Not at the moment. I submitted an Issue on PDE for that so that hopefully PDE team could take a look. I will take a short look if i find a quick improvement for SWT meanwhile (my build is still running dequeuing the events after hours 😒 ).

@merks
Copy link
Contributor

merks commented Apr 28, 2022

@vogella

I have a fix but should I hold off? I recall there is a merge that should happen soon.

I'll paste the changes in org.eclipse.pde.internal.ui.editor.context.InputContextManager.resourceChanged(IResourceChangeEvent) here so I don't loose them:

	@Override
	public void resourceChanged(IResourceChangeEvent event) {
		IResourceDelta delta = event.getDelta();

		try {
			List<IFile> added = new ArrayList<>();
			List<IFile> removed = new ArrayList<>();
			delta.accept(delta1 -> {
				IResource resource = delta1.getResource();
				if (resource instanceof IFile) {
					int kind = delta1.getKind();
					if (kind == IResourceDelta.ADDED)
						added.add((IFile) resource);
					else if (kind == IResourceDelta.REMOVED)
						removed.add((IFile) resource);
					return false;
				}
				return true;
			});
			if (!added.isEmpty() || !removed.isEmpty()) {
				asyncStructureChanged(added, removed);
			}
		} catch (CoreException e) {
			PDEPlugin.logException(e);
		}
	}

	private void asyncStructureChanged(List<IFile> added, List<IFile> removed) {
		if (editor == null || editor.getEditorSite() == null)
			return;
		Shell shell = editor.getEditorSite().getShell();
		Display display = shell != null ? shell.getDisplay() : Display.getDefault();

		display.asyncExec(() -> {
			for (IFile file : added) {
				structureChanged(file, true);
			}
			for (IFile file : removed) {
				structureChanged(file, false);
			}
		});
	}

@merks
Copy link
Contributor

merks commented Apr 28, 2022

Sorry, I see the merge already happened!

@jukzi
Copy link
Contributor Author

jukzi commented Apr 28, 2022

@merks i propose you create a PDE PR for eclipse-pde/eclipse.pde#53
thanks for working on it!

@vogella
Copy link
Contributor

vogella commented Apr 28, 2022

Sorry, I see the merge already happened!

PDE merge is still pending but please do not hold of any commits to PDE, we plan to merge pde build into pde next Monday and will be able to handle any work, so do not worry.

jukzi added a commit that referenced this issue Apr 29, 2022
Device.isDisposed() is called much more frequently then dispose() and
should not be costly synchronized. The isDisposed() check in dispose()
is still synchronized so that still there will be no double dispose().
jukzi added a commit that referenced this issue May 2, 2022
To show the impact of many messages to

org.eclipse.swt.widgets.Synchronizer.addLast ()
org.eclipse.swt.graphics.Device.isDisposed ()
org.eclipse.swt.widgets.Synchronizer.asyncExec ()
org.eclipse.swt.widgets.Display.findDisplay ()
jukzi pushed a commit that referenced this issue May 2, 2022
Synchronizer.messages grew with constant size leading to O(n^2)
performance for n messages.
Also the costly "synchronized" can be avoided by using
java.util.concurrent DataStructures.

The message queue size (which is not O(1) for java.util.concurrent) is
not needed since only checks for isEmpty() are used.
mickaelistria pushed a commit that referenced this issue May 2, 2022
There is only 1 display - no need to search in an array of 4 displays
during Display.findDisplay()

It's a minor optimization for Display.findDisplay (). The greatest
bottle neck is still the "synchronized" statement.
@jukzi jukzi closed this as completed May 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants