TreeViewer lazy loading of icon only
I have a TreeViewer, it displays a list of items. My constrains are as follows:
- Each item is represented as an XML file on the disk.
- The filename of each XML file is the label for the icon ('hello.xml' -> item 'hello' in tree)
- Each XML file has a 'type', stored in the XML, this represents the Image for each item
When I do a getChildren() for by TreeViewer items I read in each XML file and generate the Object[] array from there. Up until recently this has been acceptable but it is not any more. The normal number of XML files was 10, it is now 500.
I need to improve performance. However, for a variety of reasons such as compatablity the following options are not possible
- Cannot change the format of the XML files (i.e. use a database, serialized object..)
- Cannot create an additional representation of the XML files
- Cannot change the name of the XML files (i.e 'hello.xml' -> 'hello-type.xml'
From my tests a reasonable performance is found when just getting a directory listing of the XML files and using this for the labels. The label name is the most important aspect and whilst the image type is useful its presence is not immediately required.
These requirements and constrains have lead me to the following solution:
- Initial getChilren() scans the directory of XM开发者_开发百科L files and returns a list of labels with a 'missing' type image. (getImage() = missingImage)
- getChilren() then starts up a background Job which reads in the XML files and attains the type.
- Each Object[] has a cacheImage() method which loads in the type image (so getImage() == properIcon now). This is called by the Job.
- Job then iterates over each item in the tree and calls an update(), for each item
I have implemented this and the performance leaves something to be desired. Additionally, because we have so many calls to Display.getDefault().asyncExec to do the update, the GUI (whilst still be responsive) will take time to respond to user interactions (whilst not actually locking). Are there any ways I can improve this?
The following has been considered/tried:
- Use a DeferredTreeContentManager - did not do this because I want the names first and quickly, the images can follow later at their own pace
- Performing an update() on all of the items at once / batching up items - This locks the GUI, worse so than many individual update()s
Try lowering the priority of your background thread by calling
this.setPriority(this.getPriority()-1);
in your Thread
.
500 calls to asyncExec
should not make the GUI unresponsive, if they don't do more than setting an Image in a TreeItem
. To ensure, that your calls to TreeViewer.update
are cheap, call
TreeViewer.setUseHashlookup(true)
which enables efficient mapping of elements to TreeItems.
Wouldn't it help, if you load all images to an ImageRegistry in a background job (possibly using the XML), and then simply adding the images from this registry?
I would recommend the following optimization strategies (in order)...
Batch update calls. That is, have a list into which you deposit content objects whose image has been calculated. When the list size arrives at a certain threshold (or you run out of objects), kick off an update of all the objects presently in the list. Make sure to copy the contents of the list first and clear the list so that the caching thread can keep accumulating. Play around with the threshold parameter and you will likely get the perf you are after by reducing the number of asyncExec calls.
If #1 is not sufficient by itself (unlikely), you may also want to consider optimizing around the viewable region. Tree class has api around tracking which items are visible and you can listen for scroll events. The idea here is to prioritize image calculation for items that are visible and to call update on items only after they've become visible.
I would suggest loading/creating several images at once in the background thread (from what you said it sounds like you could do at least 10 w/o a performance issue). So your thread would look something like:
// Load/Create some number of images...
Display.asyncExcec(new Runnable() {
public void run() {
getTreeViewer().setRedraw(false);
// Loop and call update on each node you loaded the image for
getTreeViewer().setRedraw(true);
}
});
精彩评论