Table of Contents
This appendix lists some suggestions on how to get the best possible performance from your evolutionary Java programs. Much of the advice here applies whether or not you are using the Watchmaker Framework to develop your evolutionary programs.
As with all optimisations in software development, the golden rule is don't do it unless you have a demonstrable need for improved performance. Optimisations often introduce complexity and make code harder to maintain. Before starting on any optimisations, always use a profiler to identify the bottlenecks in your application. This will pinpoint the areas where optimisations are most likely to beneficial. It is pointless to expend effort to try to speed up a routine that accounts for only 0.1% of the CPU time.
For most non-trivial evolutionary algorithms, the bulk of the work is the evaluation of candidate solutions. For this reason the fitness function is often the obvious place to make improvements. A fitness evaluator should do no more work than is absolutely necessary on each invocation. If there is some initialisation that is repeated unnecessarily, consider moving it to the constructor. If similar calculations are performed every time, consider pre-computing the possible results and using a look-up table. When you consider that the evaluator may be invoked millions of times in a single run, it is clear that even small optimisations to the fitness function may add up to substantial reductions in running time.
In some evolutionary programs individuals can survive from generation to generation unmodified. The most obvious example of this is elitism. Individuals that are preserved through elitism will appear unaltered in the next generation and may survive for many generations. Individuals may also survive without modification if the evolutionary operators in use are probabilistic and don't always affect every candidate.
If fitness evaluations are expensive, it is wasteful to repeatedly recalculate fitness values
for unaltered individuals. The Watchmaker Framework provides the
org.uncommons.watchmaker.framework.CachingFitnessEvaluator
class to
address this problem. It acts as a wrapper for your fitness evaluator and caches the results
of fitness calculations. If the same candidate is evaluated twice, the cached value is returned
the second time thus avoiding the cost of recalculating the fitness score. The cache uses Java's
weak references to avoid memory leakage (if the candidate does not survive, the associated cache
entry will also be garbage collected).
Caching of fitness scores is provided as an option rather than as the default Watchmaker Framework behaviour because caching is only valid when fitness evaluations are isolated and repeatable. An isolated fitness evaluation is one where the result depends only upon the candidate being evaluated. This is not the case when candidates are evaluated against the other members of the population. Caching should not be used if it is possible for multiple evaluations of the same candidate to return different scores.