Bug 374987 - org.eclipse.swt.graphics.TextLayout.getPreviousOffset(int,int) has poor performance when text has '\t' symbols.
Summary: org.eclipse.swt.graphics.TextLayout.getPreviousOffset(int,int) has poor perfo...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.2   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-SWT-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: triaged
Depends on:
Blocks:
 
Reported: 2012-03-21 16:50 EDT by Slava Kabanovich CLA
Modified: 2019-02-03 10:57 EST (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Slava Kabanovich CLA 2012-03-21 16:50:50 EDT
Build Identifier: Version: 3.7.0 Build id: I20110613-1736

Run this code in Workbench UI thread:

    TextLayout layout = new TextLayout(Display.getDefault());
    String text = "";
    String word = "abcde";
    int l = word.length();
    for (int n = 1; n < 30; n++) {
        text += "\t" + word;
        layout.setText(text);
        long t = System.currentTimeMillis();
        layout.getPreviousOffset(text.length(), SWT.MOVEMENT_CLUSTER);
        long dt = System.currentTimeMillis() - t;
        System.out.println("Time spent (ms): " + dt + ", constant = " + dt * 1d / (l * n * n));
    }

On my computer, I got constant=0.056 so that T=0.056*L*N^2 (ms), where T is the time for executing getPreviousOffset(int,int), L is the average length of word, N is the number of words in the text. At L=5 and N=100, time is about 3 seconds.

Next, consider CTabFolderRenderer.shortenText(GC, String, int, String), which has this code:
    while (end > 0) {
        text = text.substring(0, end);
        int l = gc.textExtent(text, FLAGS).x;
        if (l + ellipseWidth <= width) {
            break;
        }
        end = layout.getPreviousOffset(end, SWT.MOVEMENT_CLUSTER);
    }
In all cases that I observed, getPreviousOffset(end, SWT.MOVEMENT_CLUSTER) returned end-1, so that time consumed by this cycle is T=0.10*L^2*N^3 (for L=5, N=100 that makes 42 minutes), here constant is 0.10 instead of 0.05 because GC.textExtent(String,int) has the same poor performance. 

Of course, usually name for tab folder is selected reasonably short and without \t symbols, but assume an application that dynamically generates tab folder name from some arbitrary user data and relies on SWT rendering will efficiently shorten too long names. 
I think that whatever performance of TextLayout and GC may be, search in CTabFolderRenderer.shortenText for a maximal substring that fits into the provided space should be bisectional rather than cutting off one symbol at a time.


Reproducible: Always
Comment 1 Slava Kabanovich CLA 2012-03-21 20:11:07 EDT
Consider CLabel.shortenText(GC, String, int). This method does implement the bisectional search for a maximal substring that fits into the
provided space, and T ~ L*N^2 * log(L*N). For L=5, N=100 I got 2 minutes, which is much better than performance of CTabFolderRenderer.shortenText(GC, String, int, String). However, CLabel.shortenText(GC, String, int) is called through paint listener, so that Eclipse will hang 2 minutes after each paint event, which is sent on many occasions. Besides, it is more probable that a text with '\t' characters will be set to CLabel, e.g. a programmer has to read an internationalized constant from a resource bundle and may not modify it, or he wants to use the fact that character '\t' is rendered by CLabel into a wider space between words than a whitespace.
Comment 2 Slava Kabanovich CLA 2012-03-22 20:58:14 EDT
Finally, I have considered one more case, StyledText.getWordPrevious(int, int, boolean), where this performance would really be fatal. But in this method the call TextLayout.getPreviousOffset works quite fast, and why? It turns out that TextLayout instance prepared by StyledTextRenderer.getTextLayout has field int[] tabs set. That makes all the difference. Add line
layout.setTabs(new int[]{12});
to my code in description above, and its performance will improve 1000 times.
Consequently,

1. There is a way to make TextLayout.getPreviousOffset(int,int) run fast. Please consider if it is a bug or a feature for it to have poor performance when property 'tabs' is not set. 

2. Methods CTabFolderRenderer.shortenText(GC, String, int, String)
CLabel.shortenText(GC, String, int) should set property 'tabs' on TextLayout instance.
Comment 3 Eric Williams CLA 2018-11-30 11:40:17 EST
Is there an SWT snippet that goes with this ticket?
Comment 4 Alexander Kurtakov CLA 2019-02-03 10:57:26 EST
No interest from reporter. Please reopen when you are ready to provide pure swt snippet to ease work on the bug.