Caching in on the Frames Array - Mike Pope
Published By | Date | Version | Knowledge Level | Keywords |
---|---|---|---|---|
Sprezzatura Ltd | 01 MAY 1992 | 2.12+ | EXPERT | CACHE, FRAMES, ARRAY, XLATE, MEMORY, MENU.BUFFER, TEMPLATE.READ, PURGE.CACHE, FLUSH, TEMPLATE.FLUSH |
Quiz time: what do the ROS filing system, window and menu templates, and XLATE all have in common? The answer – not an obvious one – is that all three take advantage of a little-known system cache known as the frames array.
The frames array is a 49-element dimensioned array in the system common block. It is initialised when you log in, and then loaded by the three processes just named, each of which reserves for itself a different block out of the 49 elements. Though you probably don't know it, you get the indirect benefit of this cache every day. And as with any cache, you also pay certain penalties. Below I'll explain who uses the cache and why and when, the pros and cons of the cache, and what influence you can have on it.
The ROS Filing System
If your memory and history with Revelation stretches that far, you may remember that Revelation C was released in the days of dual-floppy systems. Disk I/O was painfully slow, so each access had to be worthwhile. The ROS filing system was designed, therefore, to read frames of up to 64K at a time. To further minimize hitting the disk, it was efficient to cache the frames, and thus was born the frames array.
Both RTP51 and RTP59 in Advanced Revelation still use 10 elements of the frames array. For each record I/O operation, the filing system first looks to see if the necessary frame is in the array. If not, it is fetched from disk, loaded into the array, and then used for subsequent I/O. The frame's "name" – its DOS file name – is kept in a separate system common variable. At strategic times the cached frames are written out to disk and the cache cleared.
Window and Menu Templates
When Advanced Revelation came on the scene with its new interface, efforts were made to optimise window and menu loading. One way was to expand the number of elements in the frames array and stash templates there. The process that reads templates maintains up to 26 templates in memory, and uses another element to store the template names for a total of 27 elements in the frames array. Any templates accessed after the 26th aren't cached.
XLATE
The frames array is also used by RTP16, the XLATE processor. XLATE is a particularly good candidate for a cache, because it is, obviously, a read-only operation. Moreover, often one XLATE request for a field is followed shortly by another to fetch a second field from the same record. RTP16 therefore caches up to 9 records in the frames array. If this limit is exceeded (not often, as I'll explain), RTP16 throws records away on a least-recently-used (LRU) basis. (The system uses XLATE a lot, so you may not get all 9 elements to yourself.) In addition to using the frames array, RTP16 uses a couple of other system variables as arrays of names and to manage the LRU flags.
Pros and Cons
In all cases, the goal of caching records in the frames array is speed; the idea is to avoid having to go to disk. For ROS files and XLATEs, because these are often used for repeated accesses, the savings can be substantial. The benefit for templates is nowhere near as great, but noticeable when traversing a menu, for instance – just pull down a menu from the action bar and press the right arrow key, then try to imagine life without caching.
Caching has its drawbacks, however. The first is that it uses memory. With up to 10 frames of up to 64K each, ROS files can potentially use lots of memory. This is one filing system, therefore, that implements the BFS FLUSH code seriously: whenever an R/BASIC FLUSH is executed, ROS writes all "dirty" frames out to disk, and clears its portion of the frames array. Likewise, while window and menu templates are not usually huge, 26 of them hanging around in memory can be a problem, as can the 9 records being stored by XLATE.
A second problem is that of currency. If RTP16 is able to fulfil its request for a record from the cache, great; but what if the record has changed on disk since the last time RTP16 read it? You may also have experienced a currency problem if you've ever attempted to use a program that dynamically updates window or menu templates after they've displayed. You'll find that changes may not take immediate effect. What happens, obviously, is that the system is reading the cached (old) version from the frames array, not your new version on disk.
Fortunately, the system is sensitive to both of these issues. Any low-memory condition causes a BFS flush, and this clears ROS frames out immediately. But even normal processing keeps things under control. Whenever you return to TCL from any command, the system executes a BFS flush automatically. In addition, certain TCL verbs – ATTACH, DETACH, LOGTO, and sometimes compilation, including of dictionary items – go so far as to clear out the entire frames array. As you'd imagine, panic-button commands like FLUSH and RESET also do some flushing, too.
Currency is handled to some extent as well. In ROS files it's not a problem, as the filing system writes to the cache in addition to reading from it (ROS files should never be used for shared data on a net!). Cached window and menu templates can be out of synch with disk versions, but in practice this doesn't come up very often, and you can control this yourself anyway (see below).
XLATE is more interesting. It turns out that it is difficult to go behind RTP16's back. Whenever you execute a WRITE or WRITEV statement, these operations set a flag that causes the next call to RTP16 to throw away the entire current XLATE cache and start again. This virtually guarantees that your XLATEs will get a current version after you update a record (and is why, as I mentioned above, you rarely overrun the 9-record limit). On a network, no; your WRITE isn't detected by my XLATE, so I might still cache old data.
Using the Frames Array
What can you do with the frames array directly? Well, you can clear it in a couple of ways to regain memory. Call the system subroutine PURGE.CACHE (no arguments) to clear the whole lot. Execute a FLUSH first if you use ROS files. Call TEMPLATE.FLUSH (again no arguments) to clear only the templates portion of the frames array, useful not just to clear memory, but to force the system to re-read changed templates from disk. (Actually a wee bug in current versions can cause the system to be confused about how many templates are buffered, leaving undetected templates in memory. TEMPLATE.FLUSH will clear all buffers, though, avoiding any resulting problems with memory loss). (As noted, any WRITE operation clears the XLATE cache.) The TCL verb FLUSH does a FLUSH plus PURGE.CACHE; TCL RESET (level 2+) calls TEMPLATE.FLUSH.
More interestingly, you can also update or read the cache directly using TEMPLATE.READ:
TEMPLATE = TEMPLATE.READ( FILENAME, TEMPLATE_NAME )
This fetches not just a template, but any record from the cache, or from disk if necessary (updating the cache). FILENAME can be an @FM-array of files to search until the template/record is found. For menus specifically, use the function MENU.BUFFER:
MENU = MENU.BUFFER( MENU_NAME, MENU_PTR )
This searches MENUS then SYS.MENUS for the menu (using TEMPLATE.READ, so it gets cached). MENU_PTR returns the position of the menu within the labelled common list of menu names.
Curiosities
Ponder these. It's easy to clear the frames array itself. But both ROS and XLATE maintain separate variables for control information (names, LRU info); no routines in the system clear these. These variables are not big, but they do represent memory loss. Also, count up the number of elements used by the processes discussed. There are 49 elements in the frames arrays, but only 46 appear to be used. What happened here? Looks like one of those mysteries, at least for now.
(Volume 4, Issue 1, Pages 8,9)