Human Factors of XR: Using Human Factors to Design XR Systems
Performance and Memory Tuning - Part II - Transcript.pdf
1. Performance & Memory Tuning - Part II
We'll start with common strategies and performance pitfalls. This list is by no means exhaustive but it represents a lot of the experience we've had over the years.
3. label.setText("Loading, please wait...");
startThread(() -> {
performSlowCode();
callSerially(() -> label.setText("Finished!"));
}, "Loading Thread").start();
Delegate EDT to Thread
A simple example of this would be something like this we launch a separate thread to run slow code and then return the response to the EDT with callSerially
4. label.setText("Loading, please wait...");
invokeAndBlock(() -> {
performSlowCode();
});
label.setText("Finished!");
invokeAndBlock
One approach to offload processing is invokeAndBlock which spawns a thread while blocking the EDT in a non-disruptive way. This makes development very convenient
as you can avoid nested callbacks.
However, `invokeAndBlock` is more expensive in terms of performance than a regular thread & callback approach. So if you are concerned about performance
invokeAndBlock might not be the best option.
These concerns should also apply to the addToQueueAndWait method from the NetworkManager which would also incur a similar cost.
5. callSeriallyOnIdle(() -> addShadowToComponent());
callSeriallyOnIdle
One of the tricks I did in the Uber application for features such as shadow was leverage the callSeriallyOnIdle method. This method works like callSerially but it invokes
the code only when the EDT is idle.
Idle state happens when the EDT is ready to sleep and doesn't have too much to do. Initially I rendered the component without a shadow and added the shadow after
the fact using a call like this.
7. Image img = createImage(...);
Label imageLabel = new Label(img);
Caching Strategies
Caching is at the core of almost every optimization you will make. Caching can be as simple as keeping a value in a variable or as complex as weak references with
purge strategies.
A core decision about caching is where to cache and what to cache. Lets say I have this code… Should we cache the image or should we cache the label?
There is no simple answer. Normally I wouldn't cache the Label preferring to cache the Form but the logic of Image caching is tricky...
If I keep an image and then create a scaled version of that image I'll effectively have 2 instances of that image in RAM. The only tip I can give about this is awareness.
Look at the things you cache and try to avoid caching in the wrong place.
Notice that a 2gb RAM device doesn't allocate that RAM to the application. The application gets far less and this should be further restricted as device OS's kill
applications that take up too much RAM
9. // save object to cache
myObjectCache = createSoftWeakRef(objectToCache);
// extract object from cache
Object hardRef = extractHardRef(myObjectCache);
if(hardRef != null) {
// data wasn't gc'd
}
Weak/Soft References
There are some elements that might be expensive to cache such as images. In these cases we want to cache the data for the performance benefit but if we don't have
enough RAM we'd rather discard it.
This is in fact the strategy taken by EncodedImage which I'll discuss later.
Java SE has builtin classes that cover a lot of the details such as WeakReference/SoftReference etc. Unfortunately soft references which are the more useful option aren't
available. The solution is available in the CN class.
Java SE also offers WeakHashMap which we refactored into com.codename1.ui.util. It uses the soft references builtin to Codename One. This class is essentially a
regular Map that can lose its value objects when memory gets low.