This final chapter of the Telecourse focuses on the performance and security aspects of your REXX procedures.
Of course, when readability, ease of writing and execution performance can go together, it's clear that you should opt for that solution.
In Appendix G. "VM/ESA REXX Performance Guidelines" we reproduce a very interesting article that appeared in the Washington Systems Center Technical Bulletin VM/ESA Release 2.1 Performance Report (GC24-5673). The main part is devoted to comparisions between coding styles and techniques, with the purpose of finding the best performer.
If you don't want to spend the time reading the whole text, then these are the general conclusions that you should remember:
Most of these rules correspond with what we have stressed throughout this course, don't they ?
We can now look into details of other methods that enhance the performance of your procedures. These are:
The sequence of operations done by the system to prepare the execution of a REXX procedure are:
Both steps can be considered system overhead for the user.
In this chapter, we will see that it is possible to reduce this overhead if we use one of the methods that store the procedure in storage(footnote 1).
If you review the CMS Command Search order, then you'll see that CMS first looks for the procedures stored in storage. So, loading in storage reduces the overhead both for the search step as for the load step.
There are two major techniques by which a procedure can be loaded into storage:
It's clear that shared segments give an advantage to all users, while the EXECLOAD method profits only to that one specific user. Let's look at the details now.
+-*--=--=----------------------------+ >>--+-EXECLoad-+--fn--ft--+------------------------------------+-------> +-EXLoad---+ ! +-=--=-------------------+ ! +-+-fm-+--+------------------------+-+ +-*--+ ! +-=--------+ ! +-execname--+----------+-+ +-exectype-+ +-(--User---------------------------+ >--+-----------------------------------+------------------------------>< ! (1) +-User---+ ! +-(-----³--------³--+------+--+---+-+ +-SYstem-+ +-Push-+ +-)-+
Use the EXECLOAD command to load an exec or XEDIT macro into storage and prepare it for execution.
Operands
fn | is the file name of the exec to be loaded. |
ft | is the file type of the exec to be loaded. |
fm | is the file mode of the exec to be loaded. The default for file mode is an asterisk (*). You must specify fm (or *) if you want to specify an execname and exectype. |
execname | is the name to be assigned to the loaded exec. The default is '=', which means the exec's present file name is to be used. |
exectype | is the type to be assigned to the loaded exec. The default is '=', which means the exec's present file type is to be used. |
user | specifies that the storage for the loaded exec is allocated from user free storage. This is the default. |
SYstem | specifies that the storage for the loaded exec is allocated from nucleus free storage. |
Push | specifies that the exec is loaded whether an exec by the same name already exists in storage. This loaded exec does not replace the existing exec. Subsequent invocation of this execname and exectype executes the most recently loaded version. Also, a subsequent EXECDROP of this execname and exectype drops the most recently loaded version. |
Specifying the following:
execload tphone exec a = xedit (system
loads the TPHONE EXEC A into nucleus free storage and assigns to it the name TPHONE XEDIT. This is the example as given in the manual, but we find it not so good. In lesson 4 we learned about the self-contained EXEC procedures, and there is thus no need to load an EXEC as if it were an XEDIT macro.
| The difference between the USER and SYSTEM attributes is important. When the procedure is loaded in the user free storage (the default), then this storage will be flushed when you abend or issue HX. If the procedure is loaded in the system or nucleus free storage, this procedure will be preserved in storage at abend or HX. |
In addition to the advantages of pre-loading the procedure in storage, the use of shared segments makes the same storage area to be shared by many users on the system. This reduces the load on the paging subsystem.
EXEC procedures and XEDIT macros can be stored in shared segments. A shared segment can contain many different procedures.
There are two different implementations of shared segments that can contain procedures:
The first type existed long before the second type was implemented, and is probably the most used and best known. The CMS Installation Segment can only contain procedures. Logical shared segments on the other hand can also contain
The CMSINST segment is generated via the DCSSGEN command, while the logical shared segments are generated via the SEGGEN command. SEGGEN needs these segment definition files:
The result of the SEGGEN command is that the shared segments are generated, but also that the names and relations are stored in the SYSTEM SEGID S file, which is used by CMS when SEGMENT LOAD/RESERVE/RELEASE/PURGE commands are executed.
We have to come back on CMS' search order for procedures, especially in the case of the procedures stored in the CMS Installation Segment (CMSINST) or Logical Shared Segments.
You should first understand this:
The complete search order for procedures is thus:
| Members of a logical shared segment can also be defined as extensions of the CMSINST segment, and are then found in step 2 instead of 1. |
So, if you have a procedure FILELIST EXEC on the S-disk, on the Y-disk, in the CMSINST shared segment and on your B-disk, the version on the B-disk will be executed, unless you issue the SET INSTSEG B command, in which case the CMSINST version will be executed.
If however, you EXECLOAD the version on the Y-disk, then this version will be executed.
The EXECMAP command can inform you of any procedure loaded in storage. Here is an example:
Name Type Usage Records Bytes Attribute Segname EXECUTE XEDIT 0 865 37632 SHARED CMSINST FILELIST EXEC 0 388 15152 SHARED CMSINST PARSE XEDIT 7 224 10080 SHARED CMSINST PEEK EXEC 1 570 25688 SHARED CMSINST PROFFLST XEDIT 0 232 11288 SHARED CMSINST PROFILE XEDIT 1 30 31016 USER PROFILE XEDIT 0 140 5312 SHARED CMSINST QUERY XEDIT 1 127 5824 USER RECEIVE XEDIT 0 35 1688 SHARED CMSINST RZLINK EXEC 2 590 608936 SHARED RZLINK RZLNKM EXEC 2 70 72296 SHARED RZLINK SETSYN XEDIT 5 94 4720 SYSTEM SPLTJOIN XEDIT 3 45 2176 USER SYSPROF EXEC 1 231 11816 SHARED CMSINST
From this output we can learn that:
| When a procedure is loaded in storage, the minidisk (or SFS directory) that contains the 'source' procedure is not required anymore. It is therefore possible to EXECLOAD a procedure through the SYSPROF EXEC, or to save it in a logical shared segment and have no other version available to the users on any of the accessed disks. This can be considered a security feature, as end-users will have great difficulty in changing the procedure (it is still possible). As we will see, compiled procedures can also be loaded in storage, and then the protection from modification is complete as a compiled procedure is totally unreadable by an end-user. |
To complete this discussion, we have to add yet another possibility for loading procedures in storage. Since VM/ESA Release 1.2.1, it is possible to define virtual minidisks which are implemented in storage. DEFINE VFB-512 is the command that allows you to do this.
Would that be the best solution to improve the performance of the procedures ? Think a while before reading on...
Virtual Disks have following drawbacks:
For procedures in shared segments, in the worst case, only a page-in operation may be required.
We hope you were convinced about the real strong points of REXX:
There are however a few weak points that make that REXX is not yet considered widely as a complete and general programming language:
These weak points can be addressed by using the IBM Compiler and Library for SAA REXX/370 (program number 5695-013)(footnote 2)
The compiler effectively can make your procedures run as fast as other compiled programs, and the source code gets hidden from the user.
To give you an idea of the gains the compiler can give, see next
table:
Compiled programs that include many... | Run this much faster... | Measured |
---|---|---|
Arithmetic operation | 6 to 10 times | 9.7 |
String and word processing | 6 to 10 times |
|
Constants and variables | 4 to 6 times | 5.8 |
References to procedures and built-in functions | 4 to 6 times | 4.9 |
Changes to values of variables | 4 to 6 times | 8.7 |
Assignments | 2 to 4 times | 25.2 |
Reused compound variables | 2 to 4 times | 4.4 |
Host commands | minimal | 1.0 |
Note: For the measured values, no coding tricks were used to favor the compiler !
Rule of thumb: expect a 3 to 4 times improvement for a REXX program that is
The results of the measurements are rather obvious when you think about it a while.
The REXX compiler has a series of inherent optimizations:
do queued() parse pull line select when left(line,1)='*' then iterate /* ignore comments */ when word(line,1)='KING' then call chess substr(line,10,20) when word(line,1) word(line,3)='FROM BOSS' then call myboss when word(line,1)='SKIP' then iterate otherwise Say 'Invalid card:' line end /* select */ end /* queued */
The word(line,1) function is executed several times, and the compiler may be able to discover this and optimize it for you.
We reproduce the most pertinent parts from a document that discusses benchmark results comparing different coding techniques:
code | Interpreted | Compiled | Factor | Note |
---|---|---|---|---|
a='1 ' or a=1e0
a='1' or a=1 | 0.555
0.550 | 0.165
0.009 | 3.4
60 | 1 |
Numeric digits 8;a=1
Numeric digits 9;a=1 Numeric digits 10;a=1 | 0.550
0.550 0.550 | 0.151
0.009 0.009 | 3.6
58.5 58.5 | 2 |
a=hello world
a=hello_world | 0.505
0.283 | 0.102
0.023 | 5.0
12.3 | 3 |
a=hello world
a='HELLO WORLD' | 0.505
0.212 | 0.102
0.010 | 5.0
20.8 | 4 |
parse value 1 with a 1 b 1 c 1 d
parse value 1 1 1 1 with a b c d a=1;b=1;c=1;d=1 | 0.938
1.182 1.009 | 0.413
0.296 0.043 | 2.3
4.0 23.4 | 5 |
a=left('1',1)
a=left('1',1);a=a+0 a=left('1',1);a=(a+0)+0 | 0.559
0.558 0.558 | 0.161
0.009 0.008 | 3.5
63.2 72.4 | 6 |
call to inner subroutine
call to outer subroutine | 0.11
2.31 | 0.02
2.45 |
| 7 |
Notes:
Almost all examples show that when you follow good coding rules, both the interpreter as the compiler profit from it. A compiled procedure is however always larger than the source version, and it will thus cost extra I/O to load it, unless you have loaded it into storage.
One final, but important remark: the compiler can not only generate compiled procedures (commonly called CEXECs(footnote 3)), but also text decks. The latter can then be generated to modules (GENMOD) or can be linked by other programs (REXX or other languages).
The compiled procedures (CEXECs) can be manipulated as regular REXX procedures (e.g. EXECLOADed or stored in shared segments).
This is the end of the course. Not everything is said about REXX, and the more you use it, the more you will discover new techniques for yourself. If you liked our course, then you may consider taking the CMS Pipelines Telecourse too. We would also appreciate some feedback from you, see our e-mail addresses in the Instructions.
(1)
If there is only one user for the procedure, and he/she executes it
only once in a session, there will be of course no gain in first loading
the procedure in storage.
Back to text
(2)
This product allows you to compile and run REXX procedures.
To run compiled REXX procedures you need only the IBM Library
for SAA REXX/370 (program number 5695-014), and this is included
free of charge in VM/ESA Version 2, Release 3.0 and subsequent.
Back to text
(3)
When a source procedure is compiled, the output has a filetype CEXEC
(or CXEDIT), and that explains the terminology. CMS, however, does
not recognize these filetypes, and it is therefore necessary to
rename the source to SEXEC and the compiled version to EXEC.
Back to text
(4)
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