The text in this chapter is an extract of the Washington Systems Center Technical Bulletin VM/ESA Release 1.2.1 Performance Report (GC24-5673).
As you will see, it confirms what we have stressed throughout this course. The document however misses some useful information, such as comparison with CMS Pipelines solutions.
REXX performance suggestions are consolidated and quantified in this section. The majority of REXX applications may not need to be scrutinized with respect to performance, but selecting the best REXX environment and using a coding style that reduces the cost of REXX scanning and execution may optimize a performance-sensitive REXX application. Execs, such as those on shared disks, which are commonly run by many users on a system, are good candidates for performance tuning.
Although not quantified here, a REXX application's performance may be improved by loading the REXX program into memory with EXECLOAD to save the cost of loading the exec in future executions or saving the exec in a shared segment to reduce overall system virtual storage requirements. Also, if the REXX compiler is installed on the system, the REXX program may be compiled to reduce run time. These suggestions and most of those that are provided later in this section apply to CMS Pipelines stages written in REXX as well.
The system under which REXX programs run includes a default environment for processing commands. Selecting the optimum REXX environment within your REXX program will reduce the amount of searching required for issuing commands.
When it begins scanning and executing a file, the REXX interpreter establishes, by default, the XEDIT environment for XEDIT macros and the CMS environment for CMS execs. For example, if you issued the CP command SP READER NOHOLD from an XEDIT macro, the default search path would be as follows:
Within your program, selecting the optimum REXX environment can reduce the amount of searching. (Issuing the command SAY ADDRESS() will tell you which environment is active). The following are three environments that REXX understands along with the order in which the command search is performed when all defaults apply:
The REXX environment can be changed temporarily or permanently to reduce the amount of searching. For example, you can temporarily change to the command environment for the ACCESS command by issuing
address 'COMMAND' 'ACCESS ...'
then for subsequent commands use the current default environment. The REXX environment can be changed permanently by setting a new default, so subsequent commands are sent to the new environment as follows:
address 'COMMAND' /* sets new default environment */ ... 'ACCESS ...' /* uses new default environment */
We will describe the performance characteristics both for changing the environment for one command and for setting a new default later in this document.
Upon entry to a function or subroutine, the environment of the caller is used and upon return, the environment of the caller is restored.
XEDIT goes through the following steps to determine if an instruction is an XEDIT instruction(footnote).
COMMAND subcommand
then only try steps 4, 5, 6.1, and 7. (This is the editor command COMMAND, not the REXX environment COMMAND).
MACRO macro
then only try step 6.2.
Note: Setting the default to MACRO ON is not functionally equivalent to prefixing with MACRO. This is because fewer areas are searched when looking for the command when it is prefixed with MACRO.
Scanning involves combining the separate characters of the file into higher constructs. For example, the DO construct would be formed from the letter D followed by the letter O during scanning. The scanning virtual path length applies just once per invocation of the REXX program. The execution virtual path length applies each time an instruction is executed. Total virtual path length represents the sum of scanning and execution path length.
REXX scans and executes a program as follows:
x = QUEUED() initiates a scan x = 'QUEUED'() looks directly for external functionNote: Using quotes suppresses a cache of the label. If the quoted code is in a loop, you may want to try it both ways (see more later).
The measurements and conclusions summarized in the following tables were based on
System defaults were used for the measurements unless otherwise specified. For example, when measuring the path length of an XEDIT macro, the XEDIT environment was used. (This is the default REXX environment for XEDIT macros).
Alternative REXX coding techniques are compared in this section. The method having the smallest total path length is considered the best and is flagged with a þ. Sometimes a second method is worth consideration. If so, it will also be flagged with the þ. The second method may, for example, have a shorter execution path length, which would be a benefit when the code is in a loop.
Alternate methods are presented for three reasons:
Data were collected using the CP TRACE facility. The collection method is described in detail at the end of this document. This method can be used to measure alternatives in your environment.
In the following tables, column headings are as follows:
Total | Total virtual path length (scanning plus execution path lengths combined). |
Exec | Execution virtual path length. |
Scan | Scanning virtual path length. |
Example | REXX code examples for which data is supplied. |
A þ highlights the best performing solution.
The following shows various ways of invoking CP commands, CMS commands, and execs from a procedure (with the default REXX environment of CMS).
The best method has the form:
address 'COMMAND' 'CP cpcmd':
and saved 8522 total path length when compared to the slowest.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
13556 | 13095 | 461 |
| SPOOL READER NOHOLD |
12753 | 12500 | 253 |
| 'SPOOL READER NOHOLD' |
8689 | 8410 | 279 |
| 'CP SPOOL READER NOHOLD' |
9005 | 8442 | 563 |
| address 'CMS' 'CP SPOOL READER NOHOLD' |
13457 | 12532 | 925 |
| address 'CMS' 'SPOOL READER NOHOLD' |
5034 | 4451 | 583 | þ | address 'COMMAND' 'CP SPOOL READER NOHOLD' |
5406 | 4789 | 617 |
| call 'DIAG' '08','SPOOL READER NOHOLD' |
5813 | 5140 | 673 |
| address 'COMMAND' 'EXECIO 0 CP (STRING SPOOL READER NOHOLD' |
Our additional comments:
The fastest method is:
address 'COMMAND' 'cmscmd'
and saved 4104 total path length when compared to the slowest.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
8365 | 8114 | 251 |
| MAKEBUF |
8156 | 8028 | 128 |
| 'MAKEBUF' |
8472 | 8060 | 412 |
| address 'CMS' 'MAKEBUF' |
4368 | 3936 | 432 | þ | address 'COMMAND' 'MAKEBUF' |
The fastest method is:
address 'COMMAND' 'EXEC exec'
and saved 21475 total path length when compared to the slowest. NULLEXEC EXEC only contained the following two lines:
/* */ exit
Total | Exec | Scan |
| Example |
---|---|---|---|---|
29344 | 28704 | 640 |
| address 'CMS' 'EXEC NULLEXEC' |
46969 | 46463 | 506 |
| CALL 'NULLEXEC' |
29094 | 28666 | 428 |
| 'EXEC NULLEXEC' |
26492 | 26041 | 451 |
| NULLEXEC |
26284 | 25951 | 333 |
| 'NULLEXEC' |
26503 | 25978 | 525 |
| address 'CMS' 'NULLEXEC' |
25494 | 24945 | 549 | þ | address 'COMMAND' 'EXEC NULLEXEC' |
Our additional comments:
It is clear now that address 'COMMAND' is the best. All commands in the next examples were coded using address 'COMMAND' but we replaced these by address '', as it saves some space in the tables.
Abbreviating the parameter (but not the command) saved 4229 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
8775 | 8308 | 467 |
| address '' 'QUERY LANGUAGE' |
12860 | 12418 | 442 |
| address '' 'Q LANGUAGE' |
12716 | 12294 | 422 |
| address '' 'Q LANG' |
8631 | 8184 | 447 | þ | address '' 'QUERY LANG' |
Scan path length for this one statement was reduced by 312 instructions
by using RECFM=V. In this section a ø represents the space
character in the example column.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
904 | 391 | 513 |
| x=1 øøøø ... øøøø (total of 80 characters)
This file was RECFM=F LRECL=80 |
592 | 391 | 201 | þ | x=1
this file was RECFM=V |
To read in an entire file, a single EXECIO DISKR with STEM option is better than multiple EXECIOs. The stem option requires more storage than holding one line at a time in memory (the VAR option). The STEM option is also available for writing (EXECIO DISKW).
Total | Exec | Scan |
| Example |
---|---|---|---|---|
64766 | 62809 | 1957 |
| do i = 1 to 5
address '' 'EXECIO 1 DISKR LINES ASSEMBLE B (VAR X' end address '' 'FINIS LINES05 ASSEMBLE B' |
30046 | 28805 | 1241 |
| address '' 'EXECIO * DISKR LINES ASSEMBLE B (STEM Y.'
address '' 'FINIS LINES05 ASSEMBLE B' |
27342 | 26626 | 716 | þ | address '' 'EXECIO * DISKR LINES ASSEMBLE B (FINIS STEM Y.' |
Using this tip saved 37424 total path length:
When multiple EXECIOs are done to same file, it is faster to keep the file open and do a single FINIS at end. Using the single FINIS saved 68421 total path length. For both examples, the two lines beginning with address were actually one long line in the test files.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
124439 | 122796 | 1643 |
| do i = 1 to 5
address '' 'EXECIO 1 DISKW LINES ASSEMBLE B (FINIS STRING TEXT' end |
56018 | 53973 | 2045 | þ | do i = 1 to 5
address '' 'EXECIO 1 DISKW LINES ASSEMBLE B (STRING TEXT' end address '' 'FINIS LINES ASSEMBLE B' |
You can see that they each cost about 4500 total path length. If you issue a MAKEBUF, put entries on the stack, always remove all those entries from the stack, and issue a DROPBUF, then you can remove the MAKEBUF/DROPBUF as it serves no useful purpose.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4496 | 4065 | 431 |
| address '' 'MAKEBUF' |
4486 | 4055 | 431 |
| address '' 'DROPBUF' |
Using the REXX VALUE() function saved 3613 total path length (7160-3547) when putting a global variable, and saved 4547 total path length (7142-2595) when getting a global variable. However, GLOBALV has the advantage when more than one variable is to be saved/retrieved because GLOBALV supports a list of variables.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
7160 | 6593 | 567 |
| address '' 'GLOBALV SELECT TESTGRP PUT GLOBVAR' |
7142 | 6575 | 567 |
| address '' 'GLOBALV SELECT TESTGRP GET GLOBVAR' |
3547 | 2692 | 855 | þ | prevvar = 'VALUE'('VALUVAR',valuvar,'GLOBAL TESTGRP') /* PUT */ |
2595 | 1932 | 663 | þ | newvar = 'VALUE'('VALUVAR',,'GLOBAL TESTGRP') /* GET */ |
By specifying the file mode, 19560 total path length was saved because CMS did not have to search all file modes.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
28151 | 27634 | 517 |
| address '' 'LISTFILE WILDCARD EXEC *' |
8591 | 8069 | 522 | þ | address '' 'LISTFILE WILDCARD EXEC B1' |
The following shows various ways of invoking CP and CMS commands, execs, XEDIT macros, and editor subcommands from XEDIT macros (with the default REXX environment of XEDIT).
The fastest method is:
'CP cpcmd'
and saved 15337 total path length when compared to the slowest.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
17691 | 17255 | 436 |
| SPOOL READER NOHOLD |
16870 | 16642 | 228 |
| 'SPOOL READER NOHOLD' |
2354 | 2075 | 279 | þ | 'CP SPOOL READER NOHOLD' |
7296 | 6733 | 563 |
| address 'CMS' 'CP SPOOL READER NOHOLD' |
11708 | 10805 | 903 |
| address 'CMS' 'SPOOL READER NOHOLD' |
3497 | 2914 | 583 |
| address '' 'CP SPOOL READER NOHOLD' |
4008 | 3391 | 617 |
| call 'DIAG' '08','SPOOL READER NOHOLD' |
4320 | 3647 | 673 |
| address '' 'EXECIO 0 CP (STRING SPOOL READER NOHOLD' |
The fastest method format is:
address '' 'cmscmd'
and saved 10977 total path length when compared to the slowest.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
13914 | 13683 | 231 |
| MAKEBUF |
13699 | 13591 | 108 |
| 'MAKEBUF' |
8950 | 8812 | 138 |
| 'CMS MAKEBUF' |
7040 | 6638 | 402 |
| address 'CMS' 'MAKEBUF' |
2937 | 2505 | 432 | þ | address '' 'MAKEBUF' |
8984 | 8806 | 178 |
| 'COMMAND CMS MAKEBUF' |
The fastest method has the form:
address '' 'EXEC exec'
and saved 21306 total path length when compared to the slowest. NULLEXEC EXEC only contained the following two lines:
/* */ exit
Total | Exec | Scan |
| Example |
---|---|---|---|---|
30812 | 30419 | 393 |
| NULLEXEC |
30604 | 30329 | 275 |
| 'NULLEXEC' |
26161 | 25860 | 301 |
| 'CMS NULLEXEC' |
23833 | 22964 | 869 |
| address 'CMS' 'NULLEXEC' |
22460 | 21922 | 538 | þ | address '' 'EXEC NULLEXEC' |
26292 | 25708 | 584 |
| address 'CMS' 'EXEC NULLEXEC' |
43766 | 43318 | 448 |
| call 'NULLEXEC' |
33265 | 32893 | 372 |
| 'EXEC NULLEXEC' |
The best format is:
'MACRO macro'
With the default setting of SET MACRO OFF, using the MACRO prefix saved 835 total path length. Similar tests also showed that the MACRO prefix helped, but not by as much, when SET MACRO ON was active.
A functional difference between using the MACRO prefix and not using the prefix is that using the prefix avoids separating of nonalphabetic characters. For example :
N2
sends parameter 2 to routine N ; however,
MACRO N2
invokes routine N2.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
8218 | 8084 | 134 |
| 'NULLMACR' |
7383 | 7219 | 164 | þ | 'MACRO NULLMACR' |
Both techniques below are very similar (total path length). If 'COMMAND EXTRACT /FNAME/' were in a loop it would have the advantage because its execution path length was slightly less. Also, if SET MACRO ON had been in effect, 'COMMAND EXTRACT /FNAME/' would have had a definite advantage because it would save searching for an XEDIT macro named EXTRACT. We will see that there is an extra advantage in abbreviating XEDIT subcommands.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
3429 | 3258 | 171 |
| 'EXTRACT /FNAME/' |
3438 | 3227 | 211 | þ | 'COMMAND EXTRACT /FNAME/' |
This example gets the logical record length of the file being edited. Use of EXTRACT saved 10684 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
11029 | 9713 | 1316 |
| address '' 'LISTFILE USEEXTR XEDIT B (FIFO DATE NOH'
parse pull p1 p2 p3 p4 lrecln . |
3457 | 3254 | 203 | þ | 'COMMAND EXTRACT /LRECL' |
Each avoidance of synonym checking saved 172 total path length (3256-3084). So, after 15 avoidances (2612/172), the new default began to help.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
3256 | 3066 | 190 |
| 'SET PF1 111' |
2612 | 3084 | 2404 |
| 'COMMAND SET SYNONYM OFF' |
2856 | 208 | 228 | þ | 'SET PF1 111' |
The last 4 commands in the second method averaged a savings of 760 total path length (15596-12558)/4.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
15596 | 14359 | 1237 |
| 'COMMAND SET PF1 111'
'COMMAND SET PF1 111' 'COMMAND SET PF1 111' 'COMMAND SET PF1 111' 'COMMAND SET PF1 111' |
12558 | 11165 | 1393 | þ | 'COMMAND SET PF1 111#',
'COMMAND SET PF1 111#', 'COMMAND SET PF1 111#', 'COMMAND SET PF1 111#', 'COMMAND SET PF1 111' |
Each avoidance saved 32 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
3260 | 3101 | 159 |
| 'COMMAND SET PF1 111' |
3228 | 3051 | 177 | þ | 'COMMAND PF1 111' |
Saved about 17 total path length per character removed: (3503-3384)/7
Total | Exec | Scan |
| Example |
---|---|---|---|---|
3503 | 3305 | 198 |
| 'COMMAND EXTRACT /FNAME/' |
3452 | 3269 | 183 |
| 'COMMAND EXTRACT /FN/' |
3384 | 3221 | 163 | þ | 'COMMAND EXT /FN/' |
Removing the 1 saved 65 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1817 | 1678 | 139 |
| 'COMMAND N 1' |
1752 | 1623 | 129 | þ | 'COMMAND N' |
In these sections a ø represents the space character in the example column.
Removing blank lines saved execution path length=37 and scan path length=59.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
96 | 37 | 59 |
| blank line |
0 | 0 | 0 | þ | no line |
Moving code to the left saved 3 scan path length per character: (216-201)/5.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
663 | 447 | 216 |
| øøøøø x=1 |
648 | 447 | 201 | þ | x=1 |
Saved 3 scan path length per character (282-267)/5.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
729 | 447 | 282 |
| x= øøøøø,
1 |
714 | 447 | 267 | þ | x=,
1 |
In this discussion "line comments" refers to lines that have just a comment (no executable code):
/* this is a line comment */
"Code comments" refers to comments that are along side the code:
x = 1 /* this is a code comment */
"Comments" refers to line comments and code comments.
Each character removed from a comment saves 5 scan path length (118-103)/3.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
250 | 147 | 103 | þ | /**/ |
255 | 147 | 108 |
| /*.*/ |
260 | 147 | 113 |
| /*..*/ |
265 | 147 | 118 |
| /*...*/ |
Code comments do not cause any additional execution path length (they do, however, cause additional scan path length).
Total | Exec | Scan |
| Example |
---|---|---|---|---|
722 | 447 | 275 |
| x=1 /*.....*/ |
648 | 447 | 201 | þ | x=1 |
Each additional line comment cost 160 total path length ((590-270)/2) and 37 execution path length ((221-147)/2).
Total | Exec | Scan |
| Example |
---|---|---|---|---|
270 | 147 | 123 | þ | /*....*/ |
430 | 184 | 246 |
| /*....*/
/*....*/ |
590 | 221 | 369 |
| /*....*/
/*....*/ /*....*/ |
Switching from three consecutive line comments to a single multiline comment (with three lines) saved 196 total path length (590-394).
Also, notice that execution path length for the multiline comment (147) is the same as for a single line comment.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
270 | 147 | 123 |
| /*....*/ |
590 | 221 | 369 |
| /*....*/
/*....*/ /*....*/ |
394 | 147 | 247 | þ | /*....
.... ....*/ |
It costs 37 execution path length to execute a line comment.
It costs 37 execution path length to execute a line comment.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
2804 | 1386 | 1418 |
| (with g=0, THEN path taken) |
2766 | 1348 | 1418 | þ | (with g=1, ELSE path taken)
if go=0 then do end else do end |
It is not faster to put the most frequent true condition first because
all three conditions are evaluated anyway.
However, this may influence the way you code.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4529 | 2709 | 1820 |
| (when a=1 b=0 c=0) |
4529 | 2709 | 1820 |
| (when a=0 b=1 c=0) |
4532 | 2712 | 1820 |
| (when a=0 b=0 c=1)
if a=1 | b=1 | c=1 then do end else do end |
From previous section, we know that it does not help to put the most frequent true condition first (when ORing the conditions). If one of the conditions (say a=1) is much more likely and the code is in a loop, then switching to a SELECT statement costs 157 total path length (4399-4556) for the first use, but saves 872 execution path length (2579-1707) for each subsequent use.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4399 | 2579 | 1820 |
| (when a=1, b=0, c=0)
if a=1 | b=1 | c=1 then do end else do end |
4450 | 1759 | 2691 |
| (when a=1, b=0, c=0)
if a=1 then do end else if b=1 | c=1 then do end else do end |
4556 | 1707 | 2849 | þ | (when a=1, b=0, c=0)
select when a=1 then do end when b=1 | c=1 then do end otherwise end |
It is not faster to put the most frequent false condition first because all three conditions are evaluated anyway. However, this may influence the way you code.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4486 | 2666 | 1820 |
| (when a=0, b=1, c=1)
if a=1 & b=1 & c=1 then do end else do end |
4486 | 2666 | 1820 |
| (when a=1, b=0, c=1)
if a=1 & b=1 & c=1 then do end else do end |
4483 | 2663 | 1820 |
| (when a=1, b=1, c=0)
if a=1 & b=1 & c=1 then do end else do end |
From previous section, we know that it does not help to put the most frequent false condition first (when ANDing the conditions). If one of the conditions (say a=0) is much more likely and the code is in a loop, then switching to a SELECT statement costs 47 total path length (4458-4505) for the first use, but saves 982 execution path length (2638-1656) for each subsequent use.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4458 | 2638 | 1820 |
|
if a=1 & b=1 & c=1 then do end else do end |
4399 | 1708 | 2691 |
|
if a=0 then do end else if b=1 & c=1 then do end else do end |
4505 | 1656 | 2849 | þ |
select when a=0 then do end when b=1 & c=1 then do end otherwise end |
By using the nested IF, it saved 350 total path length (5296-4946) when the a=1 path was taken. Savings was similar when the b=1 path was taken and when the c=1 path was taken.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4946 | 1914 | 3032 | þ | (when a=1 path taken) |
5473 | 2441 | 3032 |
| (when b=1 path taken) |
6011 | 2979 | 3032 |
| (when c=1 path taken)
if a=1 then do end else if b=1 then do end else if c=1 then do end |
5296 | 1944 | 3352 |
| (when a=1 path taken) |
5834 | 2482 | 3352 |
| (when b=1 path taken) |
6383 | 3031 | 3352 |
| (when c=1 path taken)
select when a=1 then do end when b=1 then do end when c=1 then do end end |
If you have a flag that is set to 1 for TRUE and 0 for FALSE, then using the second method saved 505 total path length. See next section, Use 'DO flag' in place of 'IF flag THEN'" when the IF statement only has a THEN path.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
2737 | 1321 | 1416 |
|
if a=1 /* given a=1 */ then do end else do end |
2232 | 906 | 1326 | þ |
if a /* given a=1 */ then do end else do end |
If you have a flag that is set to 1 for TRUE and 0 for FALSE, then the using the second method saved 388 total path length. See previous section, Use IF flag THEN ... ELSE when the IF statement has both THEN path and an ELSE path.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1615 | 644 | 971 |
|
if a /* given a=1 */ then do end |
1227 | 697 | 530 | þ |
do a /* given a=1 */ end |
Each lower path costs 660 total path length (6634-5301)/2.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
5302 | 1999 | 3303 | þ | (when a=1 path is taken) |
5968 | 2665 | 3303 |
| (when a=2 path is taken) |
6634 | 3331 | 3303 |
| (when a=3 path is taken)
select when a=1 then do end when a=2 then do end when a=3 then do end end |
To Initialize All Compound Variables, aaaa. = 5: Initializing just the stem saved 14342 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
15236 | 14175 | 1061 |
|
do i = 1 to 10 aaaa.i = 5 end |
894 | 570 | 324 | þ | aaaa. = 5 |
Using PARSE to initialize the variables saved 871 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
2978 | 1835 | 1143 |
| aaa = 1
bbb = 2 ccc = 3 ddd = 4 eee = 5 |
2107 | 1265 | 842 | þ
| parse value '1 2 3 4 5' with aaa bbb ccc ddd eee |
Using the short variable name saved 48 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
657 | 417 | 240 |
| aaaaaa=1 |
609 | 409 | 200 | þ | a=1 |
In vara.aaaa.bbbb, vara. is the stem, and aaaa and bbbb might be variables, so REXX must do a variable look-up for aaaa and bbbb.
If aaaa and bbbb are not really variables, it was faster to use vara.1aaa.1bbb because the digit 1 caused REXX to suppress the variable lookup. The example saved 19 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
871 | 549 | 322 |
| vara.aaaa.bbbb = 'data' |
852 | 530 | 322 | þ | vara.1aaa.1bbb = 'data' |
Using PARSE VAR saved 3708 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
14700 | 13235 | 1465 |
|
a = 'WORD1 WORD2 WORD3 WORD4 WORD5' do i = 1 to 'WORDS'(a) x = 'WORD'(a,i) end |
10992 | 9560 | 1432 | þ |
a = 'WORD1 WORD2 WORD3 WORD4 WORD5' do i = 1 to 'WORDS'(a) parse var a x a end |
It may pay to set a new default REXX environment (as in the second example) rather than to keep overriding an existing REXX environment (as in the first example).
Setting a new default environment cost 482 total path length, and each time the new default was used saved 336 (4383-4047). The savings started with the second use.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4383 | 4071 | 312 |
| address '' 'MAKEBUF' |
482 | 217 | 265 |
| address '' |
4047 | 4039 | 8 | þ | 'MAKEBUF' |
Using the built in function saved 7631 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
9448 | 8299 | 1149 |
|
n=10 text = '' do n text = text'*' end |
1817 | 1232 | 585 | þ |
n=10 text = 'COPIES'('*',n) |
Removing the loop saved 8797 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
12413 | 10961 | 1452 |
|
sumi = 0 do i = 1 to 5 sumi = sumi + a.i end |
3616 | 2821 | 795 | þ | sumi = a.1 + a.2 + a.3 + a.4 + a.5 |
This example sets b to a 4 digit number with leading zeros (with a=23, b will be set to 0023). Combining statements saved 665 total path length. See next section for an even better way of performing the function.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
3532 | 2484 | 1048 |
| b='RIGHT'(a,4)
b='TRANSLATE'(b,'0',' ') |
2867 | 2019 | 848 | þ | b='TRANSLATE'('RIGHT'(a,4),'0',' ') |
This is a refinement of "Combine Statements Where Logic Permits." The second example below takes advantage of a third parameter of the RIGHT() function (the TRANSLATE() was no longer needed). The savings was 868 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
2867 | 2019 | 848 |
| b='TRANSLATE'('RIGHT'(a,4),'0',' ') |
1999 | 1427 | 572 | þ | b='RIGHT'(a,4,'0') |
The two comparison operators function as follows:
The net of the above is that they are different. In the example below the exact value of the operand was known so the strictly equal operand, '==', saved 4 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1667 | 835 | 832 |
| if answer = 'YES' then nop /* with answer='YES' */ |
1663 | 794 | 869 | þ | if answer == 'YES' then nop /* with answer='YES' */ |
When concatenation without an intervening blank is desired, removing the concatenation operator saved 93 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1040 | 644 | 396 |
| a = 'pre'||line |
947 | 625 | 322 | þ | a = 'pre'line |
When concatenation with intervening blank is desired, removing the concatenation operator saved 410 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1326 | 808 | 518 |
| a = 'pre'||' '||line |
916 | 601 | 315 | þ | a = 'pre' line |
Scanning of unexecuted code can cause significant unnecessary overhead. If your REXX program has code that is infrequently executed (but scanned), the infrequently executed code could be made into a routine and either externalized or moved to the bottom (this could require the practice of quoted function/subroutine calls).
When the less likely event does occur (the code is executed) the path length would be longer, but with infrequent use this could be an excellent trade-off.
To get a feel for the cost of the scanning, look at the scan path length column in the measurement tables. You will notice that it is a fairly small percentage of the cost of commands that REXX passes to other environments (CMS for example).
However, for REXX instructions, scan path length can be a significant percentage. The largest percentage improvement would be for programs that are heavy with REXX instructions and do not have many loops.
Use of the quoted function/subroutine suppresses a cache of the location of the function. Not using the quotes cost 49 execution path length (630-581) for the first invocation, but saved 34 execution path length (581-547) on subsequent invocations. Results will vary greatly with the functions used and sequence of execution. This comparison relates to execution path length so scan path length and total path length are not presented.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
| 581 |
|
| x='QUEUED'()
looks external to the program |
| 630 |
|
| x=queued()
- sets lookaside - causes scan to end of program (if not already scanned) |
| 547 |
| þ | x=queued()
uses lookaside |
Adding the quotes saved 138 total path length (4879-4741). The quotes tell REXX that it is looking at a literal string, so there is no need for REXX to search to see if a variable (MAKEBUF for example) exists. The more variables used in the REXX program, the more significant the savings would be. The test exec had just 2 variables.
Here is another example:
address COMMAND LISTFILE fn ft fm /* O.K. */ address '' 'LISTFILE' fn ft fm /* better */
You may not enclose REXX keywords in quotes. REXX keywords are in upper case in the REXX syntax diagrams:
'do' i = 1 'to' 3 /* does not work */ do i = 1 to 3 /* works */
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4879 | 4427 | 452 |
| address COMMAND MAKEBUF |
4749 | 4316 | 433 |
| address COMMAND 'MAKEBUF' |
4741 | 4318 | 423 | þ | address 'COMMAND' 'MAKEBUF' |
It took 1018 total path length to set NUMERIC DIGITS. For arithmetic in which numbers and results are low precision, dropping the precision to 5 made no difference. However, for calculations resulting in more significant digits (for example, calculating 1/3), decreasing the precision from 9 (the default) to 5 decreased total path length by 480 (2514-2034) for just this one calculation.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1018 | 592 | 426 | þ | NUMERIC DIGITS n |
1250 | 959 | 291 |
| x=1+1 /* given precision=5 */ |
1250 | 959 | 291 |
| x=1+1 /* given precision=7 */ |
1250 | 959 | 291 |
| x=1+1 /* given precision=9 */ |
2034 | 1739 | 295 | þ | x=1/3 /* given precision=5 */ |
2274 | 1979 | 295 |
| x=1/3 /* given precision=7 */ |
2514 | 2219 | 295 |
| x=1/3 /* given precision=9 */ |
Removing the semicolon at end of the line saved 23 scan path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
671 | 447 | 224 |
| x=1; |
648 | 447 | 201 | þ | x=1 |
Using LEAVE saved 1201 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
11542 | 8651 | 2891 |
|
do i = 1 to 5 if i=3 then signal done end done: say i |
10341 | 8414 | 1927 | þ |
do i = 1 to 5 if i=3 then leave end say i |
Using ITERATE saved 1500 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
13392 | 11107 | 2285 |
|
do i = 1 to 5 if i^=3 then do ... end end |
11892 | 10121 | 1771 | þ |
do i = 1 to 5 if i=3 then iterate ... end |
Total | Exec | Scan |
| Example |
---|---|---|---|---|
1122 | 819 | 303 | þ | y=y+1 /* inline code */ |
5267 | 2665 | 2602 |
| y=bump(y) /* internal routine */
the routine was: bump:return y+1 |
6171 | 3068 | 3103 |
| y=bump(y) /* internal procedure */
- the procedure was: bump:procedure expose y return y+1 - One of the reasons that the internal procedure had a longer path length than the internal routine was that a new variable tree was set up for the internal procedure. This could provide the internal procedure with an advantage if the calling routine had lot of variables (because REXX would not have to search through all the calling routine variables when doing a variable lookup). |
73050 | 70431 | 2619 |
| y=bump(y) /* external exec */
Where the external exec was: /* */ exit arg(1)+1 |
When you are using a PROCEDURE, using EXPOSE to pass a parameter saved 823 total path length.
Total | Exec | Scan |
| Example |
---|---|---|---|---|
5810 | 2393 | 3417 |
| stext = 'StringOfText'
call sub stext the procedure was: sub: procedure parse arg stext return |
4987 | 1866 | 3121 | þ | stext = 'StringOfText'
call sub the procedure was: sub: procedure expose stext return |
Overall savings is 240 total path length (5028-4788).
Total | Exec | Scan |
| Example |
---|---|---|---|---|
5028 | 4531 | 497 |
| address '' 'CP SPOOL READER HOLD' |
4956 | 4474 | 482 |
| address '' 'CP SP READER HOLD' |
4836 | 4379 | 457 |
| address '' 'CP SP R HOLD' |
4788 | 4341 | 447 | þ | address '' 'CP SP R HO' |
CMS path lengths can be obtained by placing the following instructions with the code being measured:
address '' 'CP TRACE I NOTERM' /* start counting */ address '' 'CP TRACE COUNT' /* reset count */ (test A) address '' 'CP TRACE COUNT' /* display then reset */ (test B) address '' 'CP TRACE COUNT' /* display then reset */ (test C) address '' 'CP TRACE COUNT' /* display then reset */ address '' 'CP TRACE END ALL' /* display and end */
This tracing causes your virtual machine to run much slower because of the processing required to count the CMS instructions.
When the above example is run, the first two CP TRACE instructions do not generate any output. The first three measurements displayed "Trace count is nnnn" are for the three test cases respectively. The last displayed measurement can be ignored.
The output is something like:
(test case A running here) Trace count is 7697 (test case B running here) Trace count is 7463 (test case C running here) Trace count is 7413 Trace count is 4860 Trace ended
The measurements above include path length associated with scanning and executing the instruction:
address '' 'CP TRACE COUNT' /* display then reset */
Successive instructions of this type were tested from both EXECs and XEDIT macros to obtain the following trace overhead chart:
Total | Exec | Scan |
| Example |
---|---|---|---|---|
4892 | 4289 | 603 |
| (from an EXEC) |
3331 | 2728 | 603 |
| (from an XEDIT macro) |
The path lengths are then obtained by subtracting the appropriate trace overhead from the measured results.
It is possible for the REXX application to fail during tracing. If instructions are being counted and the REXX program terminates without getting to
address '' 'CP TRACE END ALL' /* display and end */
your virtual machine will seem to be running very slowly because instructions are still being counted. To get your virtual machine back to normal, try
TRACE END ALL
BEGIN
To obtain the breakout in virtual path length between scanning and execution, the following line of code was placed at the beginning of the file (after the initial REXX comment, /* */):
arg inparm; if inparm = 'EX' then x=queued()
x=queued()
and the entire file is scanned. Therefore path lengths for subsequent instructions in the application represent execution time (execution path length) because they were scanned when attempting to find the QUEUED function.
Function and subroutine calls within the test case may cause scanning. This can be avoided by placing quotes around the name (as described in "REXX Scanning and Executing").
In this discussion, XEDIT macro is an editor command that is implemented
in an exec file with a file type of XEDIT. XEDIT subcommands are the
other commands that XEDIT understands, but are not in a file with file
type XEDIT.
Back to text