DOCUMENT #680 ======================================================================= FINE TUNING R:BASE ======================================================================= Product: R:BASE Version : 3.1C & Higher ======================================================================= Area : PERFORMANCE Category: CONFIGURATION ======================================================================= Of the many enhancements added to R:BASE, one is the ability for you, the developer, to fine tune R:BASE itself. Most often, R:BASE is able to make the right decisions about the best way to process commands; but sometimes, you know your data better and could pick a more efficient method if you were only given the opportunity. R:BASE now gives you the opportunity to tune performance. The following can be used to increase application performance: <> MANOPT setting and query optimizer control <> MICRORIM_MAXF2BLK, MICRORIM_MAXF3BLK and MICRORIM_REFRESH variables <> SET SORT parameter -H, -Z, and -K startup switches MANOPT,#TABLEORDER ================== Use the MANOPT setting in a multi-table select or with a view to explicitly specify the table order for R:BASE to use when joining tables. Before experimenting with changing the table order, you need to know the default table order, i.e. the table order R:BASE has chosen using its internal optimizing algorithm. To see the table order R:BASE used when joining the tables with SELECT, use the #TABLEORDER variable. Here's an example using Prodview from the CONCOMP sample database. Prodview is a five table view. The #TABLEORDER variable shows the table join order, and the applicable indexed columns. SET MANOPT OFF *(must be off to use R:BASE's default optimizing algorithm) SET DEBUG ON *(required to access #TABLEORDER) SELECT * FROM prodview WHERE LIMIT=1 *(viewing 1 row is sufficient to see the table order, and is fast) WRITE .#TABLEORDER employee,transmas,transdet.transid,customer.custid,product.model | | | | | table 1 | | | | no index used | | | | table 2 | | table 5 no index used | | uses index column table 3, uses | model to link with index column transid | transdetail to link with transmaster | table 4, uses index column custid to link with transmaster R:BASE looks at the size of the tables (number of rows and columns) and indexes when using the optimizing algorithm. Removing or adding indexes on linking columns will change the default table order. With Prodview, the algorithm determined it was best to start with the Employee table and link to the Transmaster table (note that #TABLEORDER displays only the first 8 characters of the table or column name only). The column empid links the two tables, but it is not indexed in either table. R:BASE then takes the result of that join and adds in the Transdetail table, but now there is an indexed column it can use. It takes the rows from the Employee/ Transmaster join and performs indexed lookups on the Transdetail table to find matching rows based on transid. The Customer and Product tables are similarly linked in. Now that the default table order R:BASE used is known, the MANOPT setting is used to change this order and determine performance. The following piece of code checks for performance differences based on the MANOPT setting. SET DEBUG ON SET MANOPT ON *(or OFF) SET VAR vstart=.#TIME OUTPUT DUMMY.DAT SELECT * FROM prodview OUTPUT SCREEN SET VAR vend = .#TIME SET VAR vdiff = (.vend-.vstart) SET VAR vtime = (RTIME(0,0,.vdiff)) SHOW MANOPT WRITE .#TABLEORDER WRITE .vtime When working with MANOPT, the important part of the view definition or SELECT command is the FROM clause. For example, the view, Prodview: View: prodview SELECT #T1.transdate,#T2.*,#T3.*,#T4.empid,#T4.empfname,#T4.emplname, #T5.prodname FROM transmaster #T1,transdetail #T2,customer #T3, employee #T4,product #T5 WHERE ((( #T1.transid = #T2.transid ) AND ( #T1.custid = #T3.custid ) AND ( #T1.empid = #T4.empid ) AND ( #T2.model = #T5.model ))) The table order in the FROM clause of the view definition is transmaster,transdetail,customer,employee,product. With MANOPT ON, R:BASE uses the table order as specified in the FROM clause to join the tables. Notice that the table order in the FROM clause is different from the default table order selected by the query optimizer. Here's what #TABLEORDER shows when we have MANOPT ON and select from Prodview: transmas,transdet.transid,customer.custid,employee,product.model This is different from the order selected by R:BASE's query optimizer. R:BASE now uses tables in the order specified in the SELECT's FROM clause, incorporating applicable indexed columns. By changing the order of the tables in the FROM clause you can change the order R:BASE joins the tables for possible improved performance. The article "Optimizing Application Code for Speed" in the May/June 1992 Exchange Technical Journal has examples of how table order affects the performance of a SELECT. In most instances, R:BASE chooses the most efficient way to join your tables; but you can use the MANOPT setting to experiment and see if a different table order is better. In addition, in R:BASE 4.0A, the query optimizer has been enhanced to perform a query based on a "first row fastest" or "overall result set fastest" algorithm. Certain commands, like Browse/edit, always use the "first row fastest" algorithm for a quick data display. From the R> prompt, the SET LINES setting forces R:BASE to use one algorithm or the other. SET LINES 0 uses the "overall result set fastest" algorithm; any other setting for LINES uses the "first row fastest" algorithm. When printing a report based on a view (a stored SELECT), set R:BASE to use the "overall result set fastest" algorithm if you are printing to a file or a spooled printer. Use the "first row fastest" algorithm if you are printing to a non-spooled printer. SET SORT, SHOW SORT =================== SET SORT ON is available in RBASE 3.x and 4.0. Other SET SORT parameters are available only in R:BASE 4.0A in the 386/486 version. If there is not enough memory space to do a sort, R:BASE creates temporary sort files (.$$$). The SET SORT ON parameter optimizes disk space usage. This is helpful if you get the message "Unable to create sort file. Check for full disk." Versions of R:BASE prior to 4.0A do not have the option to increase the amount of memory used for sorting, it is limited to 64K. With 4.0A 386/486, you have the new options SET SORT MAX, and SET SORT MIN . R:BASE runs in extended memory and as such has a large pool of memory available for sorting (as opposed to conventional memory, with only 640 K total memory, sort space was limited to 64K). By default, with 4.0A, sorting uses all available extended memory. You can use the SET SORT MAX and the SET SORT MIN parameters to allocate memory for sorting; setting maximum and minimum limits, respectively. Before using either of these parameters, use SHOW SORT LAST. This shows the amount of memory (in Kb) that R:BASE calculated would be needed to sort in memory (no disk swapping) the data from the last executed command. This is a calculated amount, it is not the actual amount of memory used. Execute your large sorts or print your reports and then do SHOW SORT LAST. You'll see how much memory to allocate with SET SORT. Make sure your SET SORT MAX is at least the size shown by SHOW SORT LAST. Use SHOW SORT MAX, SHOW SORT MIN to display the current settings. The default value for both is 0, R:BASE uses all available extended memory. -H Startup Option ================== The -H startup option is available in the 286 version of R:BASE only. By default, R:BASE uses 300 memory handles in the 286 version. There are situations when this is not enough. Two of the most common are reports with lookups (each lookup takes on average 6 memory handles) and rules (each rule takes on average 6 memory handles). If you get the error message "Out of dynamic memory handles. Check application complexity.", you may want to try starting R:BASE with the -H option. This lets you increase the number of memory handles R:BASE can use. For example, RBASE -H320, starts R:BASE and allows use of 320 memory handles. The number of additional memory handles is limited by the size of the memory area where the memory handle entries are allocated. You cannot allocate more than 460 memory handles. If you need more than this, you should examine your application and use of features requiring the memory handles. -K Startup Option ================= The -K startup option is available in the 386/486 version of 4.0A only. Another 4.0A parameter you can use to tune R:BASE is the -K startup option. In prior versions of R:BASE, R:BASE would grab 64K of memory for its data area (processing expressions, while loops, sorts etc.), then when it needed more memory it would grab another 64K chunk. With 4.0A, the default has changed to 128K; but the -K option gives you the opportunity to increase that amount. You can tell R:BASE to grab 1024K (1 Mb) of memory right off the bat for processing, and memory continues to be allocated in 1 Mb pieces. For example, RBASE -K1024, allocates memory in 1 Mb chunks. Use this option ONLY if you consistently have "Out of dynamic space" errors or if your OS/2 SWAP file continually grows. -Z Startup Option ================= The -Z startup option is available in the 386/486 version of R:BASE only. By default, R:BASE (386/486) does not use any conventional memory for processing. When it needs additional memory, it allocates it from extended memory only. All available conventional memory can thus be used by other programs for ZIPping. This conventional memory is also used by CodeLock and Gateway (Import/export). If R:BASE runs out of extended memory to allocate, you can tell R:BASE to use conventional memory, if needed, by using the -Z startup option. With the -Z option you specify an amount of memory (in Kb) to reserve, i.e. you specify the amount of conventional memory R:BASE will not attempt to use. For example, RBASE -Z10 allows R:BASE to use all but 10 Kb of available conventional memory. RBASE -Z200 uses all but 200 Kb of conventional memory. MICRORIM_MAXF2BLK, MICRORIM_MAXF3BLK ==================================== When RBASE 4.0 (386/486) needs to access data from the disk, it must switch from protected mode back to real mode. This can degrade performance because of the amount of switching back and forth that R:BASE must do. By default, R:BASE uses six buffers for file 2 (data) and six buffers for file 3 (indexes). The file 2 buffers are 5120 bytes each in the 286 version and 8192 bytes each in the 386/486 version. The file 3 buffers are 512 bytes each regardless of version. In single-user mode you can increase the number of default buffers by using the system variables MICRORIM_MAXF2BLK, MICRORIM_ MAXF3BLK. SET VAR MICRORIM_MAXF2BLK=n SET VAR MICRORIM_MAXF3BLK=n "n" is the number of buffers to allocate. The maximum number of buffers is 500. Don't forget that each additional buffer takes memory, and also a memory handle in the 286 version. The number of buffers you can set is limited by your available memory. You must set these variables before you connect a database. Setting the buffers higher allows R:BASE to read from disk and hold in memory larger amount of data. For example, setting MICRORIM_MAXF2BLK to 20 means you can hold 20*8192 = 163,840 bytes of data from file 2 in memory in the 386/486 version and 20*5120 = 102,400 bytes of data in the 286 version. Setting MICRORIM_MAXF3BLK to 30 means you can have 30*512 = 15360 bytes of index (file 3) information in memory at one time. Increasing your file 2 buffers helps multi-table joins, particularly if one of the smaller tables can be held entirely in memory. Increasing your file 3 buffers also helps because indexes are used in joining the tables. Sorting, single sequential passes through the data and index building with CREATE INDEX are not accelerated by increasing the buffers. Multi-user mode ignores these variables because of concurrent access to data by various users. A large amount of data held in memory by one user is not appropriately updated by other users. MICRORIM_REFRESH ================ The variable, MICRORIM_REFRESH, new in 4.0A, must be used with caution. In a multi-user environment, it specifies the amount of time before data in local memory buffers is re-read from disk. Normally, all data is refreshed after every command. If you are doing repetitive selections (reads) from the same table(s) in a multi-user system, you can see dramatic performance improvements by setting this variable. For example, SET VAR MICRORIM_REFRESH=30, to flush the local buffers and re-read information from disk every 30 seconds. To reset to the default, simply CLEAR VAR MICRORIM_REFRESH. This feature should only be used in situations where you are performing read-only operations on a multi-user database and are not concerned with viewing updates made by other users immediately. Do not use this feature in situations where the database could be updated by multiple people. Which Ones To Use ================= The options detailed above can help you reach maximum performance with R:BASE. But, since each application and database is a unique set, only a trial and error process will identify which options help in your situation. Don't forget that different hardware configurations and available memory also affect performance. Experiment with the above options and see which ones improve performance for your appli- cation. You, the developer, now have the opportunity the set R:BASE internal parameters to gain optimum performance for each application.