====================================================================== FINDING YOUR OPTIMUM RTVMCONV - AN EMPIRICAL APPROACH ====================================================================== PRODUCT : R:BASE VERSION : 3.1 or higher CATEGORY : MEMORY SUBCATEGORY : AREAS & MANAGERS ====================================================================== From William B. Driskell, 6536 20th Ave. N.E., Seattle, WA 98115. Bill is a marine biologist, a computer consultant, an active participant in the Seattle-area R:BASE users group, and a frequent contributor to the R:BASE EXCHANGE. Each of the following three techniques can improve performance by optimizing R:BASE's use of memory resources: <> Modifying your system's memory and disk configurations <> Modifying your program code to minimize your program's need for memory <> Modifying your memory allocations by adjusting the RTVMCONV setting in the DOS environment The first item is nicely covered in the MEMORY.TXT file that comes with R:BASE, and the second item was discussed in HOW R:BASE ORGANIZES & MANAGES MEMORY, PART I in the July/August 1991 R:BASE EXCHANGE. This article, part II in the memory management series, teaches you how to use performance indicators and the ISTAT functions to find your optimum RTVMCONV setting. Memory Review ============= First, here's a brief review of R:BASE's memory scheme from the previous article. R:BASE is loaded into conventional memory in four areas: <> Static code area <> Static data area <> Dynamic code area <> Dynamic data area The static areas comprise the actual R:BASE program and some fixed data and together take up nearly 300K of memory. The dynamic code area and dynamic data area contain sections of the R:BASE program and buffers of the database. Each dynamic area gets slices of the remaining memory. In the metaphor of a workshop, the dynamic data area is workbench for R:BASE and the dynamic code area is the toolbox. To extend the metaphor, the static code area contains the power tools and the static data area holds the stock inventory. Each dynamic area has a manager program that constantly swaps or discards working copies of pieces of R:BASE's program code (RBASE.EXE) or the database's data to use the allocated memory space effectively. Functionally, the dynamic data manager and dynamic code manager act like parking lot valets. The RTVMCONV Setting ==================== Although the static areas are not adjustable, you can adjust the memory allocation for the dynamic areas because it's controlled by the RTVMCONV setting in the DOS environment. The default RTVMCONV setting is 640,150 for R:BASE 3.1B and 512,128 for R:BASE 3.1A. When you start R:BASE, R:BASE checks the current RTVMCONV setting to determine how much of the remaining memory to allocate to each of the dynamic areas. For example, if the RTVMCONV setting is 640,150, R:BASE knows it must reserve 150K of the available memory for the dynamic data area and then give the rest of the available memory, up to 640K, to the dynamic code area. Typically, only about 220K of memory is actually available after the static areas are loaded, so with a setting of 640,150, the dynamic code manager would get only about 70K. More space reserved for dynamic data to hold, for example, a lot of complex report expressions means less space for dynamic code. When less space is available for dynamic code, more swapping occurs. That takes time, so your R:BASE applications might run more slowly. If you don't need so much space for the complex forms or reports (remember, forms and reports are data in your database), you can reduce the data setting to 128K by exiting from R:BASE and entering the following at the DOS prompt with no spaces around the equal sign: SET RTVMCONV=640,128 The Optimal Allocation ====================== To find the optimal allocation for your application, you need to understand the trade-offs between performance and complexity. A larger dynamic data area (set by a larger second number in the RTVMCONV) gives R:BASE a larger workbench but a smaller toolbox. With a larger second number, R:BASE can fit a more complex structure, form, or report on the workbench but will have to do a lot of running to the store room to get tools that will fit into the smaller toolbox. But if the data set is large and a complex sorted query is run, the dynamic data manager needs the larger workbench to handle the data. In other words, a smaller second number (representing the dynamic data area) leaves more room for the dynamic code area, thus, R:BASE will usually be faster because it doesn't have to swap pieces of RBASE.EXE as often. But the second number (representing the dynamic data area) must be large enough to run your application without running out of memory. Because the total dynamic memory allocation is a rob-Peter-to-pay-Paul situation, anticipating the optimal memory balance might seem virtually impossible. But let's try an empirical solution to this dilemma. The Empirical Timing Approach ============================= To optimize a single application, you can empirically adjust the RTVMCONV value simply by using execution time as a performance indicator. That is, run your program noting the execution time, exit to DOS, adjust the RTVMCONV setting, and then rerun the program. After several trials, you will be able to fine-tune your RTVMCONV to minimize execution time. If you set the value for data allocation (the second number) too low, your program will stop with Out of dynamic space or Out of memory error messages. If you set it too high, the dynamic code manager is constrained by churning code through the limited space, severely degrading performance. By setting up time variables at the beginning and end of a routine with R:BASE's #TIME system variable, you can calculate the elapsed time of execution. This process is appropriate only for command files and reports, not for interactive forms. Here's an example: SET VAR vbegin TIME = .#TIME *( ...) *( Your R:BASE program goes here.) *( ...) SET VAR vstop TIME = .#TIME WRITE .vbegin, .vstop Using a small command file, I was able to reap a 25% time savings resulting from a reduction in the dynamic data area's allocation from the 150K default to 120K. The routine ran out of memory when the value reached 80K. For the sake of future data expansion, I finally used 120 rather than 90 for the optimal dynamic data area setting (the second number). This type of optimization can be particularly useful for large applications that seem to grind on for hours. Even a small difference might yield significant time savings. Using ISTAT to Measure Memory Use ================================= Using a form to enter or edit data is an interactive process, so the empirical timing technique would be imprecise in assessing the form's performance. But in some cases, the technique can still be valuable in terms of timing an overall task. An alternative approach, discussed below, assesses the actual memory demands by using ISTAT to get rough indicators. The ISTAT() Functions ===================== The ISTAT functions were introduced in R:BASE 3.1B as a way to check database size, the amount of disk space remaining, and memory status. Database size and disk space are self explanatory; memory status is a bit more complex. ISTAT can return four status checks on conventional memory: <> ISTAT('MEMORY') the amount of free memory (in bytes) remaining to the operating system. <> ISTAT('TOTALALLOC') the amount of memory allocated for the dynamic data area that has been used so far in the session. This value might be less than the second number in the RTMVCONV setting if the data manager hasn't requested the maximum reserved. <> ISTAT('TOTALFREE') the amount of memory free inside the current dynamic data area. <> ISTAT('MAXFREE') the size of the largest single memory block free inside the current dynamic data area. You can get a quick snapshot of the disk, the database, and your system's memory by combining all the ISTAT functions into a single command file like this one: *( ISTATS.CMD--diagnostic routine to show memory and disk status.) CLS FROM 6 SET VAR vi INTEGER = (ISTAT('DISKSPACE')) WRITE .vi AT 6,37 USING 'Free disk space: 99,999,999 bytes' SET VAR vi = (ISTAT('DBSIZE')) WRITE .vi AT 8,39 USING 'Database size: 99,999,999 bytes' SET VAR vi = (ISTAT('MEMORY')) WRITE .vi AT 10,30 USING 'DOS free memory remaining: 999,999 bytes' SET VAR vi = (ISTAT('TOTALALLOC')) WRITE .vi AT 12,12 USING 'Total dynamic data area+ currently allocated: 999,999 bytes' SET VAR vi = (ISTAT('TOTALFREE')) WRITE .vi AT 14,21 USING 'Total dynamic data area free space:+ 999,999 bytes' SET VAR vi = (ISTAT('MAXFREE')) WRITE .vi AT 16,5 USING 'Largest contiguous free space in + dynamic data area: 999,999 bytes' CLEAR VAR vi RETURN Actual Memory Allocated Approach ================================ You can track the actual memory demands of an application, even one that uses interactive forms, by using expressions to hold the maximum amount of memory that was allocated during execution. Initialize vmax to an INTEGER and set it to zero at the top of the application: SET VAR vmax INTEGER = 0 Then use the following expressions in the application and in forms and reports to track the maximum allocation: valloc = (ISTAT('TOTALLOC')) vmax = (IFGT(.vmax,.valloc,.vmax,.valloc)) After running the application, look at the value of vmax to see the final maximum memory that was allocated during the routine. In a command file, you don't need to constantly check and store the value. Display the final ISTAT value at the end of the routine: SET VAR valloc = (ISTAT('TOTALLOC')) WRITE .valloc Once you know the maximum allocation demand in bytes, you can use it as a guiding value (it might be slightly inflated) for resetting RTVMCONV. Exit R:BASE, then reset the second value in the RTVMCONV setting to the actual maximum allocation value of the application in K bytes. (To convert from bytes to K bytes, divide the byte total by 1024 and round up.) Then restart R:BASE and rerun the application. This technique will probably result in a performance gain because you are returning unused data memory to the dynamic code manager. That is, you're taking unused workbench space and giving it to the toolbox. If the allocation is set too low, the routine will crash and R:BASE will display an Out of dynamic space error message. Unfortunately, the maximum allocation demands might not precisely reflect the dynamic data needs. This is because the dynamic data manager allocates memory in blocks of 64K whenever possible and then smaller chunks only when a full 64K block is not available. Therefore, vmax might show that 131,036 bytes (128K bytes) were allocated, but the total block might not have been used. The optimal RTVMCONV data setting probably lies somewhere in the second block between 65K and 128K. When calculating the K bytes from the ISTAT value, I usually round up to the next 10K since a 9K overhead appears to be hidden and not reflected in the ISTAT value. For example, ISTAT reports a 90K RTVMCONV data setting (640,90) as a 82,862-byte allocation. You can get an even tighter memory snapshot of a single line of code by calculating the actual amount of memory in use at that particular moment. The following example assumes that you have already initialized Microrim Technical Notes vmaxin as an INTEGER equal to zero at the top of the application. -- use of dynamic data memory SET VAR vfree INTEGER = (ISTAT('TOTALFREE')) SET VAR valloc INTEGER = (ISTAT('TOTALLOC')) SET VAR vinuse = (.alloc - .free) -- option to store max value SET VAR vmaxin = (IFGT(.maxin,.inuse,.maxin,.inuse)) Place this code right after the most complex, memory-intensive section of the routine being assessed so you don't get some misleading mid-process or finishing statistics that might not reflect the maximum usage. ZIP Effects =========== The ZIP command causes the dynamic code manager to release most of its allocated memory in order to load and execute a DOS program. As a result, upon returning to R:BASE, the ISTAT functions reflect the much-reduced memory allocation. At this point, ISTAT(TOTALALLOC) is actually showing only the current usage, not the available allocation. As more complex (more memory-hungry) commands are run, ISTAT(TOTALALLOC) increases as a result of the dynamic code manager's requests for larger allocations. The ZIP ROLLOUT and ZIP RETURN commands both show a similar effect by releasing both dynamic and static memory areas. In this case, R:BASE copies its internal environment into a .$$$ archive file and then shells out to DOS. Conventional memory retains only a kernel of the R:BASE static code about 15K versus the full 295K. The 15K is just enough to reload the environment and restart the session. After returning to R:BASE, the ISTAT(TOTALALLOC) value is again minimal but increases as more complex operations are executed. Forcing the Data Manager ======================== Under certain circumstances, the data manager might be unable to free up enough space to accomplish an operation. For example, during a session in an accounting application when a user switches tasks from data entry to reports on benefits, the data manager might need to remove more from its buffers than normal operations generally request; the data manager might not be able to find the space to release, so it returns an error message instead. Under these circumstances, the user can force the data manager to clear its buffers by disconnecting and then reconnecting the database or by using the ZIP ROLLOUT or ZIP RETURN commands. Other Optimizing Techniques =========================== It's the function of the dynamic code manager to shuffle pages of data and code into and out of memory. Therefore, the configuration of extended or expanded memory and the use of memory management utilities, RAM disks, and disk caches can have significant effects on R:BASE performance. Refer to the memory.txt file on your latest R:BASE installation disks for Microrim's latest recommendations.