Analysis
Overview
The goal of this group of tasks is to improve the performance of the ProLib.
This revision of the task is responsible for improving the performance of ProLists.
Task requirements
There are too many notifications for updates when using a ProList.
Currently, when an AutoProperty depends on a ProList, it gets recomputed every time anything about the ProList changes - its size, its elements, etc. In certain cases, though, an AutoProperty might depend only on the size of a given ProList and thus in doesn't need to get recomputed when some elements in the ProList change their value, because this operation doesn't change the size of the list.
Another case is when you deal with skins. Skins are basically maps between keys and values. At a certain time an AutoProperty might be interested to monitor only the list of all pairs of keys and values which have the key "meddle". So if this list remains the same while the whole ProList which contains it changes, this AutoProperty shouldn't get recomputed.
- Devise and implement a concept how to deal with such problems.
- Refactor ProList, BaseProList and ListEntry code to fix design issues from previous tasks.
- Refactor all client code to meet new implementation.
- Think of tests which measure new performance against the current.
Task result
- New functionality code
- Nice unit tests
Implementation idea
Introduce aspects.
Related
How to demo
- Show previous unit tests don't break
- Show performance improvement
Design
Refactoring of code written in previous tasks will be done like this:
- ListEntry will become an Immutable to prevent users from modifying the key or value of a ListEntry which has been added to a ProList (basically, just stick to the rule that our goal in Sophie is to deal with either Immutables or ProObjects)
- findAll() and findOne() methods of the ProList<E> interface will now return List<E> and E respectively, not List<ListEntry> and ListEntry.
- the internal indexing Map in BaseProList and the corresponding methods for manipulating it will be refactored in sync with the above two design changes.
Solving the notification issues will be accomplished with the introduction of Aspects:
- an Aspect is a BasePro, so listeners can be attached and detached to/from it.
- BaseProList will use Aspects internally to reduce the attached listeners and the registered reads. This way:
- lots of reads should be registered not with the BaseProList itself but with an Aspect of the BaseProList, and so lots of listeners will be attached to the Aspects, instead of the BaseProList
- ProChanges will be fired from the Aspects of the BaseProList to registered listeners, instead of always firing from the BaseProList itself.
At first, there will be two types of Aspects:
- A size Aspect which AutoTrackers interested in only the size of a BaseProList will attach listeners to.
- A set of key Aspects. When using findOne() and findAll() methods of a ProList, a key is used to find all elements of the BaseProList which have the given key. So a key Aspect for each set of elements having the same key will be present and listeners will be attached to this Aspect, instead of the BaseProList. When the BaseProList changes but this set doesn't, an AutoTracker which depends only on this set won't get updated.
Additional things about implementation which need to be mentioned:
- Aspects will always synchronize their state with the state of the BaseProList they are associated with.
- Each Aspect will have an owner - the BaseProList it belongs to. ProLib Registry's endReadTrack() method will be modified to filter out Aspects which were read but also had their owner read. This will prevent an AutoTracker which reads let's say both a given BaseProList and one of its Aspects, to get updated multiple times. This will prevent "constant" AutoProperties which should get computed only once, to get updated more than 1 time.
Another issue which could be fixed is the performance of the findMethodAnnotation(...) methods in the ProLib ReflectionUtil class. The
public static <T extends Annotation> T findMethodAnnotation(Method m, Class<T> annotationClass);
simply maps a Class to some Method. Instead of doing this each time, a helper HashMap can be used to perform memoization of these queries.
Implementation
On my machine, these optimizations led to the following results:
- 4-5 sec startup time reduction for the usage of Aspects.
- roughly the same for the memoization described above.
Prior these optimizations, startup time was ~25 sec. --gogov 2009-05-09 13:31
Merged to the trunk in [2521] --meddle 2009-05-11 15:20
Testing
(Place the testing results here.)
Comments
(Write comments for this or later revisions here.)
Attachments
-
2009-05-09-15-29 patch
(24.0 KB) -
added by gogov 16 years ago.
latest patch. size aspect works!
-
2009-05-08-21-22 patch
(45.3 KB) -
added by gogov 16 years ago.
old patch