Controlling When Result Values Are Cached
Caching of Computed Results
When Analytica computes the result of a variable, it stores the computed value in memory. If that result is requested later, it simply returns the previously computed value without having to recompute it. This process is referred to as caching. The stored result is the cached result.
After Analytica has cached a computed result, it maintains information about what factors went into that computation. If any upstream value is changed, Analytica automatically detects that its cached value may no longer be valid. When this happens, Analytica invalidates the cached result. Only the variables that are or may be influenced by a changed parameter are invalidated. After a cached value is invalidated, that value is no longer stored in memory, and thus next time it is needed, Analytica must recompute it. This system of remembering what each result depends on is referred to as dependency maintenance. As a user of Analytica, you seldom need to worry about making sure values get recomputed when things change -- the dependency maintenance subsystem takes care of it for you automatically.
Analytica actually caches up to three separate results for each variable: The mid value, sample value and index value. Some of these may not apply to particular variables. In practice, it is the mid value and sample value that have the potential to consume subtantial amounts of memory.
Many benefits emerge from the automatic caching of results. Some computations are automatically made dramatically more efficient, since intermediate variables don't need to be recomputed by every child variable that uses the result. In some dramatic cases this can actually convert an exponential algorithm into a polynomial one, with no effort from the modeler (in effect, it automatically converts to a dynamic programming algorithm). When you are examining or debugging the results of a model, the ability to see the values of intermediate variables can be extremely helpful. Not only are these readily accessible without additional recomputation, but you see exactly the values (including the precise Monte Carlo samples) that were used during the computation of downstream results.
Analytica caches results at the resolution of variables. Intermediate results used during the evaluation of a definition live only as long as their values are used, and then their memory is reclaimed. It is only when a variable object's mid or sample value completes is that value cached. These are stored in the midValue and probValue attributes of the object. Only variable objects, and objects subclassed from Variable (Decision, Chance, Objective, Index, Constant) are cached. In particular, the values of User-Defined Functions are never cached, even if they have zero input parameters.
Controlling which results get cached
One aspect of automatic caching of results is that it essentially trades memory for speed. The downside arises when available memory is limited. Caching all intermediate results in your model consumes memory, which may limit the scale of computation you can complete without the available memory resources of your computer. By eliminating some cached results, it may be possible to free up memory for use elsewhere in your model. This is the motivation for controlling what gets cached.
You can impact how much data is cached through various restructurings of your model. You seldom want to do this if you can avoid it, since it may impact transparency of your model. But when in desperation, such restructurings are sometimes warranted.
Restructurings intended to reduce the amount of cached data are based on principles. The first is that only final results of variables are cached -- not intermediate results. The second is that User-Defined Functions never cache their results. Thus, when you want to eliminate the memory caching overhead of an intermediate variable, you can fold it into the expression of its successor and eliminate the variable, or you can just convert it to a User-Defined Function.
Converting to a User-Defined function is easy in theory, since a UDF does not have to have any parameters. With a blank parameter list, it simply evaluates its definition, which can depend on other global input variables. To ensure equivalence with a variable node, your result should not have an implicit dimension (since a UDF cannot promote this to a self-index, given that it doesn't cache its results).
The mechanics of converting a variable to be a UDF are a bit more problematic. There is no user-interface method for converting the class of an object from Variable to Function. In general, you need to do this by adding a function node, copying your definition to it, changing each child of your variable to use the function instead (which involves a syntactic change as well -- adding parenthesis after the function name), and then deleting your original object.
Before going to the effort of enacting these restructurings, it is always wise to run the Performance Profiler to measure the amount of memory consumed by your intermediate variables' caches. There is no reason to eliminate variables that do not consume substantial amounts of memory.
Enable comment auto-refresher