Bug 52899 - [misc] Unacceptably poor performance using Replace dialog under SWT/GTK
Summary: [misc] Unacceptably poor performance using Replace dialog under SWT/GTK
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Text (show other bugs)
Version: 3.0   Edit
Hardware: PC Linux
: P2 normal with 2 votes (vote)
Target Milestone: 3.0 M8   Edit
Assignee: Platform-Text-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: performance
Depends on:
Blocks:
 
Reported: 2004-02-24 01:48 EST by John Zoetebier CLA
Modified: 2004-03-27 08:42 EST (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description John Zoetebier CLA 2004-02-24 01:48:16 EST
This bug report is a fork of bug https://bugs.eclipse.org/bugs/show_bug.cgi?
id=37683
A test have been done to illustrate the completely unacceptable performance of 
the Java editor using file plugins/org.eclipse.swt/Eclipse SWT
Custom Widgets/common/org/eclipse/swt/custom/StyledText.java
A change of "a" to "a1" took about half an hour on a Linux SuSE 8.2 PC with AMD 
2000+ processor with 512 Mb memory. Eclipse was choking and used over 90% CPU.
The same test on a Windows 2000 PC with AMD 1G processor and 256 Mb memory took 
less than 20 seconds.

<quote from steve_northover@ca.ibm.com>
The issue here is that Eclipse is relying on the Windows version of the 
method setRedraw() to throw away all of the individual drawing requests.  This 
is interesting issue and needs to be followed up with them.  It may be that 
they will not be able to rely on this behavior or that SWT can provide a 
partial implementation of setRedraw() to fix this particular problem.
</quote from steve_northover@ca.ibm.com>
Comment 1 Dani Megert CLA 2004-02-24 03:07:36 EST
We have done lots of performance improvements after M6. So, it would be good if
you attach the build id in which you see this behavior.

Tom, please verify that this scenario.
Comment 2 John Zoetebier CLA 2004-02-24 03:42:07 EST
I repeated the test with Eclipse 3.0 M7
Platform 1: 
SuSE Linux 8.2
AMD 2000+
Eclipse running with 256 Mb RAM

Platform 2: 
Windows 2000
AMD 1000
Eclipse running with 200 Mb RAM

Performance on Windows acceptable: about 20 seconds.
Performance on Linux has improved since M6.
However it is still like watching grass grow.
After a couple of minutes less than 10% was done: still around 30 minutes for 
this change.

To give an idea of how slow the Eclipse editor is I repeated the same test with 
Kwrite.
Kwrite did the change in the blink of an eye, I estimate about 0.2 second.
Netbeans 3.5.1 did test in 64 seconds: about 30 times faster than Eclipse 
SWT/GTK



Comment 3 Dani Megert CLA 2004-02-24 03:54:12 EST
>A change of "a" to "a1" took about half an hour
Do you mean find/replace all "a" with "a1"?
Comment 4 John Zoetebier CLA 2004-02-24 04:19:14 EST
>Do you mean find/replace all "a" with "a1"?

Exactly, find/replace all "a" with "a1".
Comment 5 John Zoetebier CLA 2004-02-25 16:17:03 EST
Some other useful information from https://bugs.eclipse.org/bugs/show_bug.cgi?
id=37683#c147

I did a test with gedit2-2.2.0.1-29 written in C using GTK.
I clocked 44 seconds for "a" to "a1" replaceall in StyledText.
This is horrendously slow compared with 0.2 second in KWrite.
It is even slow compared with 64 seconds of Netbeans 3.5.1 considering that I 
did not switch off backgound compilation.

Comment 6 Steve Northover CLA 2004-02-25 16:37:09 EST
I suspect that we are forcing the screen to be updated (either Eclipse is 
doing this or SWT is doing this as part of scroll()).  Under Windows, when 
setRedraw() is off, nothing happens.  On platforms that don't have this 
capability, you see every draw.
Comment 7 Tom Hofmann CLA 2004-02-26 05:12:03 EST
The described problem is real and the replace-speed is not acceptable. Moving
back to inbox for general discussion.

I believe the only thing we can do from the editor team would be to not rely on
Control.setRedraw(boolean) (see bug 26153 for Dougs patch to Control) and
implement it ourselves, possibly by temporarily disconnecting the model from the
viewer. 

Not sure whether this is feasible.
Comment 8 Steve Northover CLA 2004-02-26 10:33:46 EST
I have traced this down to StyledText calling scroll() and scroll() causing an 
update.  We are looking at fixing it.  I hacked something in that didn't draw 
but performance did not improve.  It might be because string measuring is slow 
on GTK (fast on Windows) and StyledText is still doing all the work to 
determinte where to draw even though nothing is actually drawn.  Fixing 
setRedraw() will not fix this problem because string measuring still has to 
work even though drawing is turned off.

RADICAL IDEA:  How about changing text "replace all" so that it relaces all 
the text in a string then resets the string into the widget?  This is what 
IBM/Smalltalk and VisualAge for Java did.  Seem to make more sense to mea and 
would make everything faster on every platform.
Comment 9 Steve Northover CLA 2004-02-26 10:34:49 EST
BTW: Dougs patch is incomplete and won't fix this problem.
Comment 10 Aaron Digulla CLA 2004-02-26 18:40:08 EST
Complex components like StyledText need a way to start a "long operation" 
where the model can be changed in an arbitrary way. GTK had "freeze" and 
"thaw" for this in 1.2 which wasn't optimal but worked. 
 
As for Dougs patch, I think this goes into the right general direction. Being 
able to remember rendering operations would solve some problems but it would 
also cause a whole lot of new problems (there must be a redraw-queue and if 
only a small part of the text component is changed, the whole thing will 
flicker). 
Comment 11 Steve Northover CLA 2004-02-26 18:46:49 EST
I tried the GDK window freeze and thaw operations and they do not help.  
Doug's patch will not help in this exact case.  Either fixing the replace to 
show only the final result (Platform/Text) or hacking a partial implementation 
of setRedraw() and speeding up string operations (Platform/SWT) will fix this 
particular problem.
Comment 12 John Zoetebier CLA 2004-03-01 16:33:06 EST
Did "replaceall a to a1" test using anjuta-1.0.0-119 on StyledText.
It took about 48 seconds.
This is slower than Eclipse on Windows with a processor half the speed of the 
test Linux box.
Furthermore, Eclipse is running other tasks in the background.
Worst of all, after finishing the replace task Anjuta is hanging with 40% User 
CPU.
The Anjuta route does not look very promissing to me. We are replacing one 
problem with more problems.
Comment 13 Aaron Digulla CLA 2004-03-01 16:53:10 EST
Is it possible to detach the model of the text view during a "replace all"? Or 
is the MVC not implemented in the StyledText component? 
Comment 14 Aaron Digulla CLA 2004-03-01 17:00:10 EST
While looking at the code of StyledText, I noticed that the LineCache (which 
seems to store the widths of all the lines) is cleared (partially) during a 
redraw(...) which seems kind of strange. 
 
I mean: Why should a refresh of the screen invalidate the length of the lines 
in the cache? 
 
Since calculating of line widths is an expensive operation in Pango(GTK), I'd 
suggest to match the clearing of the cache with the changes of the document 
model and only recalculate line sizes when the line in the model changes. 
 
And if there is some other code which expects redraw(...) to invalidate the 
cache, I'd suggest to split the code in order to speed up screen refreshes. 
Comment 15 Steve Northover CLA 2004-03-01 17:04:50 EST
Aaron, read comment #8 (RADICAL IDEA).
Comment 16 Steve Northover CLA 2004-03-01 17:09:26 EST
Daniel, can someone from Text look into this?  Replacing occurences is 
fundamental behavior for an IDE.  I have already done some analysis that shows 
that a hack implementation of setRedraw() on GTK doesn't help that much.  I am 
willing to do a more formal analysis to be sure but I think the fundamental 
problem is that you should not be showing partial results, as the strings are 
replaced. Thanks.
Comment 17 Aaron Digulla CLA 2004-03-01 17:13:46 EST
Re #8: I've read that comment but I thought that the document class 
(StyledTextContent?) is somehow involved and I wanted something which works 
even when the content is not a text. 
 
Therefore the idea: Before ReplaceAll, either unregister the viewer from the 
content or tell the content not to send update events. I think the former will 
be more simple to implement but the latter might be more thorough (think of an 
indexer in the background: That thread should also wait until all S&Rs are 
done). 
Comment 18 Aaron Digulla CLA 2004-03-01 17:17:18 EST
Re #17: Not to mention using regexps during S&R and other nifty stuff which we 
don't want to implement two times for Replace/Find and ReplaceAll :-) 
Comment 19 Stefan Zechmeister CLA 2004-03-01 17:21:22 EST
the replacement test using scite editor (see http://scintilla.org) configured to
use antialiasing takes about 10 seconds on an ibm thinkpad t21 (p3 800mhz, 384mb
ram, fedora core 1)
the scite editor uses the same editor widget as anjuta -> scintilla.org.
maybe someone can look at that as scintilla does things different.
it uses pango_layout_get_line(layout,0)/gdk_draw_layout_line(drawable, gc, x,
ybase, pll);
Comment 20 John Zoetebier CLA 2004-03-01 17:57:49 EST
Stefan,
I repeated the ReplaceAll test with scite-1.50-27 editor.
It took 6 seconds on SuSe Linux 8.2 with AMD 2000+ processor.
Font settings in User Options did not make any difference.
This is a lot faster than Anjuta and Eclipse on Windows.
There must be something on Anjuta that makes Scite work slower.
Comment 21 Tom Hofmann CLA 2004-03-01 18:18:26 EST
The text team is tracking this.

Steve, in comment 8 you wrote:
> Fixing setRedraw() will not fix this problem because string measuring
> still has to work even though drawing is turned off.

Why is this so? To compute the bounds of the document for the scrollbars? 

Currently, on platforms other than GTK, TextViewer::setRedraw(false) *does*
disconnect the model (a Document implementing IDocumentAdapterExtension) from
the widget via TextViewer::disableRedrawing. On GTK, this call has been disabled
to avoid flashing the top of the editor because StyledText::setRedraw does not
work. 

Replace-All on a document in sequential rewrite mode (which is what replace-all
uses) is pretty much the same as replace-all on a string.

I will do some measurements when re-enabling TextViewer::setRedraw, any of you
who have posted numbers are encouraged to do the same.
Comment 22 Tom Hofmann CLA 2004-03-02 03:34:27 EST
Just as a first teaser: when re-enabling setRedraw, replace-all of a to a1 in
StyledText (no regex, no-incremental, 11000 sth. matches) took less than 5 seconds.

This is on a fairly fast machine (P4 2.4G) on rh 9.
Comment 23 Steve Northover CLA 2004-03-02 10:07:05 EST
>> Fixing setRedraw() will not fix this problem because string measuring
>> still has to work even though drawing is turned off.
>
> Why is this so? To compute the bounds of the document for the scrollbars?

If anyone (StyledText included) calls GC.stringExtent() or GC.textExtent(), we 
still need to measure the string and return a result, regardless of setRedraw
().  All my quick hack did was stop drawing (which is all that Windows does).

> the widget via TextViewer::disableRedrawing. On GTK, this call
> has been disabled to avoid flashing the top of the editor because
> StyledText::setRedraw does not work. 

Why on earth do you need to go to the top of the editor all the time and then 
(I assume) back again?  Is it something that StyledText is doing to you for 
free?  Not doing this will make things faster on all platforms as well and 
will also fix the bug that occasionally (on Windows where setRedraw() is 
supported), I type something (often the hot key that comments a block of code) 
and the text editor scrolls to a different place for no reason.
Comment 24 Tom Hofmann CLA 2004-03-02 11:05:22 EST
> Why on earth do you need to go to the top of the editor all the time and then
> (I assume) back again? Is it something that StyledText is doing to you for 
> free?

When re-enabling drawing, the model does not replay the changes that have
occurred in the meantime, but simply informs the widget that the entire content
has changed. StyledText will flash the top of the file when doing that. This
gets masked by setRedraw on Windows, but not on GTK. 
Comment 25 Aaron Digulla CLA 2004-03-02 14:42:05 EST
Re comment 24: 
 
> StyledText will flash the top of the file when doing that. 
 
Just to make sure that I understand you correctly: When I so setRedraw(true), 
StyledText will scroll to the beginning of the file, draw all visible lines 
and then scroll back down to the current view position and draw all (now) 
visible lines again? 
 
Why is that? 
 
Also, how about my comment 14? I'm pretty sure that optimizing the LineCache 
would boost the performance of StyledText by an order of magnitude. 
Comment 26 Steve Northover CLA 2004-03-02 15:54:53 EST
I think we (SWT) is "flashing to the top" for free in a StyledText.  I am 
investigating further.

WRT comment #14.  We are considering more caching in GC.  StyledText itself is 
being rewritten to use the new "TextLayout" classes as part of the BIDI work 
so hacking it now might not be worth it.
Comment 27 Aaron Digulla CLA 2004-03-02 16:20:34 EST
I've created a simple benchmark for StyledText which you might want to use as  
a base to create a benchmark for this bug here. See  
https://bugs.eclipse.org/bugs/show_bug.cgi?id=51693#c32  
Comment 28 Tom Hofmann CLA 2004-03-02 16:34:52 EST
The reason for the flash is quite simple, I believe: when the content of
StyledText is reset, it will display the new content, and will do so at the top
position. 

Only after that, the viewer resets the scroll position using
StyledText::setTopPixel since it knows that the new content is pretty close to
the old one. 

StyledText cannot know this. What it could do is trying to restore the previous
scroll position when its content is reset.
Comment 29 Steve Northover CLA 2004-03-02 17:07:21 EST
Yes but I thought setText() and setTopIndex() would simple damage the entire 
area, causing a single paint for both operations.  I am investigating why this 
is not the case.
Comment 30 Steve Northover CLA 2004-03-04 10:05:58 EST
StyledText could be change to try to restore the contents to the closest 
position but this would break people that relied on the old behavior.  I 
believe that you should release the code that "disconnects the model" for 
the "replaceAll" case and we will concentrate on getting rid of the "scrolling 
to top" behavior.  If we can't get rid of that (we will), the worst case will 
be it flashes instead of taking 40,000 years to do the replace, making us look 
like fools.

Here is a simple example that shows the "scrolling to the top" (ASIDE: It 
scrolls on GTK but is very fast so you hardly see it on my machine.  Is it the 
syntax coloring that makes the equivalent Java Editor so slow?).  I have some 
fixes to SWT that make the "scrolling to top" in this example go away.  I will 
release them shortly after I have verified that they don't break anything.  
After that, you can verify that "scrolling to top" is gone for you and we can 
close this darn bug report.  Agree?

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.custom.*;

public class PR_52899a {
	
public static void main(String[] args) {
	Display display = new Display ();
	Shell shell = new Shell();
	shell.setLayout (new FillLayout ());
	final StyledText text = new StyledText (shell, SWT.V_SCROLL | 
SWT.H_SCROLL);
	shell.setSize (250, 250);
	for (int i=0; i<512; i++) {
		text.append (i + "This is a line of text in a StyledText.\n");
	}
	text.setTopIndex (128);
	Button button = new Button (shell, SWT.PUSH);
	button.addListener (SWT.Selection, new Listener () {
		public void handleEvent (Event event) {
			text.setRedraw (false);
			String string = text.getText ();
			int index = text.getTopIndex ();
			text.setText (string);
			text.setTopIndex (index);
			text.setRedraw (true);
		}
	});
	shell.open ();
	while (!shell.isDisposed ()) {
		if (!display.readAndDispatch ()) display.sleep ();
	}
	display.dispose ();
}

}
Comment 31 Tom Hofmann CLA 2004-03-04 11:23:32 EST
> I believe that you should release the code that "disconnects the model" for 
> the "replaceAll" case 

I am quite reluctant to release this before we have something working, because I
believe the "flash" is much more of a nuisance than the slightly artificial case
of replacing a with a1 in StyledText.java. I can't just reenable this for
"replaceAll".

Note that we call setRedraw a lot, namely when indenting code (Smart Tab),
toggling comments, formatting,... basically anywhere we do more than just
inserting a little text. This will interfere a lot more with my daily editing
than the replaceAll story. 

I will be happy to take out the GTK-bypass and release it once we have solved
the flash problem. All it takes is commenting out two lines.

> Is it the 
> syntax coloring that makes the equivalent Java Editor so slow?

I guess. Syntax coloring does use some cycles and probably does make the flash
more prominent.

> After that, you can verify that "scrolling to top" is gone for you and we
> can close this darn bug report.

All right!
Comment 32 Steve Northover CLA 2004-03-04 14:22:47 EST
The SWT fix has been released on all platforms.  It involves changing 
Canvas.scroll() to avoid doing and update() when asked to copy bits that are 
not visible.  StyledText uses scroll() in the implementation of setTopIndex().

NOTE: This fixes the problem in the test case PR_52899a but may not fix 
Eclipse.  Control.setRedraw() is still unimplemented (see bug #53791).

>> I believe that you should release the code that "disconnects the model"
>> for the "replaceAll" case 
>
> I am quite reluctant to release this before we have something working,
> because I believe the "flash" is much more of a nuisance than the slightly
> artificial case of replacing a with a1 in StyledText.java. I can't just
> reenable this for "replaceAll".
> 
> Note that we call setRedraw a lot, namely when indenting code (Smart Tab),
> toggling comments, formatting,... basically anywhere we do more than just
> inserting a little text. This will interfere a lot more with my daily
> editing than the replaceAll story.

1) There is nothing artificial about replacing one string with another string 
in a large file using an IDE.

2) Perhaps you should not be calling setRedraw() so much?  For one thing, 
setRedraw() causes the entire control to be painted even if you only change 
one pixel. In the case of "replace a with a1", it seems clear that the user 
expects the whole thing to redraw so setRedraw() makes sense.  Can you 
classify the various operations and decide in advance whether it should be 
called? What happens on Windows when you don't call it?  Does it get "slow" 
and "draw too much" like GTK.  This would be good information for us (SWT).

Thanks!
Comment 33 Tom Hofmann CLA 2004-03-04 14:41:26 EST
Will release the fix to TextViewer right tomorrow morning (CET). 

> 2) Perhaps you should not be calling setRedraw() so much?  For one thing, 
> setRedraw() causes the entire control to be painted even if you only change 
> one pixel. In the case of "replace a with a1", it seems clear that the user 
> expects the whole thing to redraw so setRedraw() makes sense.  Can you 
> classify the various operations and decide in advance whether it should be 
> called? What happens on Windows when you don't call it?  Does it get "slow" 
> and "draw too much" like GTK.  This would be good information for us (SWT).

Hm. We could go over the places where we call setRedraw and evaluate whether
it's worth the cost everywhere we do - generally we do it to avoid seeing a
multi-edition in many places (e.g. when commenting a block of code, we don't
want the insertion of "//" at the beginning of every line to be visually
inserted after each other, but rather all at the same moment).

Perhaps we could come up with a heuristic about the "magnitude" of a change -
but then, computing it should not be expensive either... I'll keep you informed
of any findings.
Comment 34 Steve Northover CLA 2004-03-04 17:13:42 EST
I think that "//" is a good case.  For example, someone could comment a couple 
of lines or comment an entire file.  I think the issue here is that you need 
to know which calls cause scrolling and which do not.  For calls that do not 
cause scrolling, setRedraw() should not be called.  Instead, the control 
should just get damaged appropriately and redraw only the damaged area during 
the next paint.

I wouldn't worry about a "heuristic magnitude" for now.  Control.setRedraw() 
is still unimplemented on GTK and Motif (there is a partial hack on Mac) so 
doing heuristics to decide not to call it on these platforms won't do much.

I hope this thing goes away soon because it is the poster boy for "GTK is 
slow".
Comment 35 Tom Hofmann CLA 2004-03-05 02:52:11 EST
released TextViewer.java, removed setRedraw bypass for gtk.
Comment 36 Steve Northover CLA 2004-03-05 12:43:13 EST
Tom, does it fix it?  If so CLOSE THIS PR!  Thanks.
Comment 37 Tom Hofmann CLA 2004-03-05 13:49:20 EST
Today's nightly failed, so there's no SWT drop I can try. We'll be fine for the
I-build.
Comment 38 Tom Hofmann CLA 2004-03-08 05:25:30 EST
fixed >= 20040306
Comment 39 Steve Northover CLA 2004-03-08 09:29:26 EST
Whoooopie!