The performance of your RPG programs may be suffering if you use the SELEC statement. The SELEC op-code is a great, recently added feature in RPG used to create multi-conditioned, multi-branching logic structures. But, unless you give a lot of thought to their structure, they can lead to wasted CPU cycles (this also applies to a series of IF-THEN-ELSE statements and to other languages having multi-branching constructs).
Understanding how SELEC statements works
Understanding how SELEC statements work is essential to improving their performance. An easy way to do this is to take a simple SELEC statement and make a similar construct using only IF-THEN-ELSE statements.
The first part of the code in Figure 1 depicts a basic SELEC statement having four branch points. The second part of the code is the equivalent construct using only IF and ELSE op-codes. In order for the program to select the proper branching point, it must sequentially check all conditions up to an including the one ultimately selected. For example, if VAL equals 'C', the program must check the first three conditions (VAL WHEQ 'A', VAL WHEQ 'B', and VAL WHEQ 'C') before choosing the third branching point.
Figure 1: How the SELEC statement works
* C MOVEL'C' VAL 1 * * C SELEC * C VAL WHEQ 'A' * Process for VAL = 'A' * C VAL WHEQ 'B' * Process for VAL = 'B' * C VAL WHEQ 'C' * Process for VAL = 'C' * C VAL WHEQ 'D' * Process for VAL = 'D' * C ENDSL * * * Equivalent to the above SELEC statement * using IF-ELSE-ENDIFs. * C VAL IFEQ 'A' * Process for VAL = 'A' * C ELSE C VAL IFEQ 'B' * Process for VAL = 'B' * C ELSE C VAL IFEQ 'C' * Process for VAL = 'C' * C ELSE C VAL IFEQ 'D' * Process for VAL = 'D' C ENDIF * C ENDIF C ENDIF C ENDIF * * * This version, which uses IF-ENDIFs, better * depicts the exact process the above SELEC * statement goes through. * C VAL IFEQ 'A' * Process for VAL = 'A' C GOTO ENDSL0 C ENDIF * C VAL IFEQ 'B' * Process for VAL = 'B' C GOTO ENDSL0 C ENDIF * C VAL IFEQ 'C' * Process for VAL = 'C' C GOTO ENDSL0 C ENDIF * C VAL IFEQ 'D' * Process for VAL = 'D' C GOTO ENDSL0 C ENDIF * * --------------- C ENDSL0 TAG * --------------- * * C MOVEL'1' *INLR
Two ways to improve performance of SELEC statements
Now that you understand how a SELEC statement is processed, we can look at ways to improve their performance. You can maximize the performance of the your SELEC structures and, hence the program itself, by following these three tips.
1. Code the most frequently "chosen" WHxx statement as the first one in the list of WHxx statements in the SELEC group. Then, order the remaining WHxx statements in order of decreasing frequency of use. This will obviously require some "guestimating."
For example, the code in Figure 2 shows how one might be tempted to set up a SELEC group to determine which function key was pressed and process accordingly. It seems logical to order the WHxx statements using the natural order of the F-keys (i.e., F1-F24).
Figure 2: Organizing a SELEC statement using the order of the function keys
* C SELEC * C *INKC WHEQ '1' * Exit. * C *INKD WHEQ '1' * Prompt. * C *INKE WHEQ '1' * Refresh. * C *INKG WHEQ '1' * Print. * C *INKL WHEQ '1' * Cancel/exit without save. * C ENDSL *
However, the F3 key (Exit) can only be used a maximum of one time during the entire program run (because, when pressed, F3 causes the program to end). Yet, using the natural ordering, this key is checked first each and every time the SELEC statement is executed, leading to wasted CPU cycles. If we determine that the most frequently used function key is F4 (Prompt) followed by F7 (Print), F5 (Refresh) and, finally, F3 (Exit) and F12 (Cancel), we should use this ordering for our WHxx statements and eliminate the unnecessary checks. The code on figure 3 shows how the SELEC group is restructured using this descending frequency of use order.
Figure 3: Organizing a SELEC statement using descending frequency of
* C SELEC * C *INKD WHEQ '1' * Prompt. * C *INKG WHEQ '1' * Print. * C *INKE WHEQ '1' * Refresh. * C *INKC WHEQ '1' * Exit. * C *INKL WHEQ '1' * Cancel/exit without save. * C ENDSL *
2. Keep the entire SELEC structure as small as possible. For example, don't put a lot of processing code between the individual WHxx statements of a SELEC group. This will cause unnecessary paging just to determine the correct WHxx statement to branch to. Instead, put the processing code in a subroutine.
The code on Figure 4 illustrates this concept. Instead of putting the code that processes the F4 key in the SELEC structure (as shown in the first part of the code), it is placed in its own subroutine which is called from within the SELEC group (as shown in the last part of the code). This keeps the entire SELEC group within a single page of memory (a page on the AS/400-iSeries is 4K -- figure approximately 40 source lines per page).
Figure 4: How to keep SELEC statements compact
* * Don't do this!!! C SELEC * C *INKD WHEQ '1' * 50 lines of code * to process the F4 * key (prompt). * . * . * . * C ENDSL * * Instead, do this... C SELEC * C *INKD WHEQ '1' C EXSR PROMPT * . * . * . * C ENDSL * * C MOVEL'1' *INLR * *--------------------------------------------------------- C PROMPT BEGSR *--------------------------------------------------------- * * 50 lines of code * to process the F4 * key (prompt). * . * . * . * C ENDSR *
How to improve performance of the IF-ELSE-ENDIF structure
The above tips can be applied to the more conventional IF-ELSE-ENDIF program structure because, as depicted in figure 1, the SELEC op-code is nothing more than a list of IF-ENDIF with implicit GOTOs at the end of each IF group directing control to the end of the SELEC structure (i.e., just after the ENDSL). Apply the tips given above to your IF-ELSE-ENDIF structures to maximize performance.
About the author: Ron Turull is editor of Inside Version 5. He has more than 20 years' experience programming for and managing AS/400-iSeries systems.