<pclass="commentary firstcommentary"><aid="SP1"class="paragraph-anchor"></a><b>§1. </b>Inform runs substantially faster if <ahref="../values-module/4-ets.html"class="internal">Chapter 4: The S-Parser (in values)</a> can cache
its findings: so, for instance, if Inform parses the text in words 507 to 511
once, it need not do so again in the same context.
</p>
<pclass="commentary">We provide a cache, then, for Preform nonterminals whose return type is
<spanclass="extract"><spanclass="extract-syntax">parse_node</span></span>. The cache takes the form of a modest ring buffer for each
of the contexts:
</p>
<preclass="definitions code-font"><spanclass="definition-keyword">define</span><spanclass="constant-syntax">MAXIMUM_CACHE_SIZE</span><spanclass="plain-syntax"></span><spanclass="constant-syntax">20</span><spanclass="plain-syntax"></span><spanclass="comment-syntax"> a Goldilocks value: too high slows us down, too low doesn't cache enough</span>
<spanclass="plain-syntax"></span><spanclass="reserved-syntax">int</span><spanclass="plain-syntax"></span><spanclass="identifier-syntax">pe_cache_size</span><spanclass="plain-syntax">; </span><spanclass="comment-syntax"> number of entries used, 0 to </span><spanclass="extract"><spanclass="extract-syntax">MAXIMUM_CACHE_SIZE</span></span>
<spanclass="plain-syntax"></span><spanclass="reserved-syntax">int</span><spanclass="plain-syntax"></span><spanclass="identifier-syntax">pe_cache_posn</span><spanclass="plain-syntax">; </span><spanclass="comment-syntax"> next write position, 0 to </span><spanclass="extract"><spanclass="extract-syntax">pe_cache_size</span></span><spanclass="comment-syntax"> minus 1</span>
<spanclass="plain-syntax"></span><spanclass="reserved-syntax">struct</span><spanclass="plain-syntax"></span><spanclass="identifier-syntax">wording</span><spanclass="plain-syntax"></span><spanclass="identifier-syntax">cached_query</span><spanclass="plain-syntax">; </span><spanclass="comment-syntax"> the word range whose parsing this is</span>
<spanclass="plain-syntax"></span><spanclass="reserved-syntax">struct</span><spanclass="plain-syntax"></span><spanclass="reserved-syntax">parse_node</span><spanclass="plain-syntax"> *</span><spanclass="identifier-syntax">cached_result</span><spanclass="plain-syntax">; </span><spanclass="comment-syntax"> and the result (quite possibly </span><spanclass="extract"><spanclass="extract-syntax">UNKNOWN_NT</span></span><spanclass="comment-syntax">)</span>
<ulclass="endnotetexts"><li>The structure expression_cache is private to this section.</li><li>The structure expression_cache_entry is private to this section.</li></ul>
<spanclass="plain-syntax"></span><spanclass="named-paragraph-container code-font"><ahref="2-spc.html#SP2_1"class="named-paragraph-link"><spanclass="named-paragraph">Check the expression cache to see if we already know the answer</span><spanclass="named-paragraph-number">2.1</span></a></span><spanclass="plain-syntax">;</span>
<spanclass="plain-syntax"></span><spanclass="named-paragraph-container code-font"><ahref="2-spc.html#SP2_2"class="named-paragraph-link"><spanclass="named-paragraph">Write the newly discovered specification to the cache for future use</span><spanclass="named-paragraph-number">2.2</span></a></span><spanclass="plain-syntax">;</span>
<pclass="commentary firstcommentary"><aid="SP2_1"class="paragraph-anchor"></a><b>§2.1. </b>The following seeks a previously cached answer:
</p>
<pclass="commentary"><spanclass="named-paragraph-container code-font"><spanclass="named-paragraph-defn">Check the expression cache to see if we already know the answer</span><spanclass="named-paragraph-number">2.1</span></span><spanclass="comment-syntax"> =</span>
<spanclass="plain-syntax"></span><ahref="2-spc.html#SP4"class="function-link"><spanclass="function-syntax">PreformCache::warn_of_changes</span></a><spanclass="plain-syntax">(); </span><spanclass="comment-syntax"> this empties all the caches</span>
<ulclass="endnotetexts"><li>This code is used in <ahref="2-spc.html#SP2">§2</a>.</li></ul>
<pclass="commentary firstcommentary"><aid="SP2_2"class="paragraph-anchor"></a><b>§2.2. </b>The cache expands until it reaches <spanclass="extract"><spanclass="extract-syntax">MAXIMUM_CACHE_SIZE</span></span>; after that,
entries are written in a position cycling through the ring. In either case
it takes <spanclass="extract"><spanclass="extract-syntax">MAXIMUM_CACHE_SIZE</span></span> further parses (not found in the cache) to
overwrite the one we put down now.
</p>
<pclass="commentary"><spanclass="named-paragraph-container code-font"><spanclass="named-paragraph-defn">Write the newly discovered specification to the cache for future use</span><spanclass="named-paragraph-number">2.2</span></span><spanclass="comment-syntax"> =</span>
<spanclass="reserved-syntax">parse_node</span><spanclass="plain-syntax"> *</span><spanclass="function-syntax">PreformCache::not_found</span><buttonclass="popup"onclick="togglePopup('usagePopup1')"><spanclass="comment-syntax">?</span><spanclass="popuptext"id="usagePopup1">Usage of <spanclass="code-font"><spanclass="function-syntax">PreformCache::not_found</span></span>:<br/><ahref="2-spc.html#SP2">§2</a></span></button><spanclass="plain-syntax">(</span><spanclass="identifier-syntax">wording</span><spanclass="plain-syntax"></span><spanclass="identifier-syntax">W</span><spanclass="plain-syntax">) {</span>
<pclass="commentary firstcommentary"><aid="SP4"class="paragraph-anchor"></a><b>§4. </b>As with all caches, we have to be careful that the information does not fall
out of date. There are two things which can go wrong: the S-node in the cache
might be altered, perhaps as a result of the type-checker trying to force a
round peg into a square hole; or the stock of Inform's defined names might
change, so that the same text now has to be read differently.
</p>
<pclass="commentary">The first problem can't be fixed here. It's tempting to try something like
flagging S-nodes which have been altered, and then ensuring that the
cache never serves up an altered result. But that fails for timing reasons —
by the time the S-node might be altered, pointers to it may exist
in multiple data structures already, because the cache might have served
it more than once by that time. (Not just a theoretical possibility — tests
show that this does, albeit rarely, happen.) The brute force solution is to
serve a copy of the cache entry, and thus never send out the same pointer
twice. But this more than doubles the memory required to store S-nodes,
which is unacceptable, and also slows Inform down, because allocating memory
for all those copies is laborious. We therefore just have to be very careful
about modifying S-nodes which have arisen from parsing.
</p>
<pclass="commentary">The second problem is easier. We require other parts of Inform which make
or unmake name definitions to warn us, by calling this routine. Definitions
are made and unmade relatively rarely, so the performance hit is small.
<spanclass="reserved-syntax">void</span><spanclass="plain-syntax"></span><spanclass="function-syntax">PreformCache::warn_of_changes</span><buttonclass="popup"onclick="togglePopup('usagePopup2')"><spanclass="comment-syntax">?</span><spanclass="popuptext"id="usagePopup2">Usage of <spanclass="code-font"><spanclass="function-syntax">PreformCache::warn_of_changes</span></span>:<br/><ahref="2-spc.html#SP2_1">§2.1</a></span></button><spanclass="plain-syntax">(</span><spanclass="reserved-syntax">void</span><spanclass="plain-syntax">) {</span>