0.9b (c) 1995 Peter Childs
Investigate the overheads of function calls
Author: Arthur Pool (see EMail Addresses)
(Statements in green color [like this] are comments from Bernd Schemmer)
(Use the included REXX program to run the test described in this text on your PC)
In the OS/2 operating system, there are several different ways a REXX program can call a function coded in REXX, each of which has implications for both performance and maintainability.
These options are discussed (briefly) in the WARP REXX on-line information (at least, in WARP Connect V3), which says (inter alia):
"... internal labels take first precedence, then built-in functions, and finally external functions. External functions and subroutines have a system-defined search order. REXX searches for external functions in the following order:
1. Functions that have been loaded into the macrospace for pre-order execution
2. Functions that are part of a function package
3. REXX functions in the current directory, with the current extension
4. REXX functions along environment PATH, with the current extension
5. REXX functions in the current directory, with the default extension
6. REXX functions along environment PATH, with the default extension
7. Functions that have been loaded into the macrospace for post-order execution. "
In practice then, the options, and their implications, are:
 Include the source code for the function in the primary source file. This should provide the best performance. However, if the function is to be called by more than one primary REXX program, this approach is undesirable in that it requires duplication of code, with consequent maintenance problems.
 Load the macro function into a REXX MacroSpace. This can be achieved using the RexxAddMacro call from C, or using the RxAddMacro function provided in the RXU utility package (I understand that WARP 4 (Merlin) provides a similar function in the REXXUTIL package but I have no experience with that; see New REXXUTIL functions in Object REXX, The functions to work on the macro space, and LoadMac.cmd for information about the REXXUTIL DLL included in Object REXX ). This approach is more or less equivalent to the EXECLOAD facility in VM/CMS. This approach should provide performance somewhere between the preceding and following approaches. It does however have some management costs:
1. you have to explicitly load the function into the macrospace - if you don't, you'll simply execute the copy from disk, which will be much slower;
2. you have to also ensure that the macrospace copy is unloaded and reloaded whenever the function's source file is modified - if you don't you'll be executing an out-of-date copy.
As noted above, functions can be loaded in either of two ways:
1. Loaded into the MacroSpace for pre-order execution (executed before disk-based files) - this should produce better performance than using disk-based functions.
2. Loaded into the MacroSpace for post-order execution (executed after disk-based files) - this should produce worse performance than using disk-based functions (below).
However, it is worthwhile to consider the meaning of current extension and default extension. When a function is loaded into the MacroSpace, it can be loaded with an extension (eg, .CMD) or without an extension. In these tests, as the primary REXX file had an extension of .CMD, it appears that the current extension is .CMD. We therefore measured the performance of functions loaded into the MacroSpace (and called) with an extension of .CMD and also without an extension.
Note however that in general one would prefer to load functons without extension so that they are equally accessible to REXX programs with any extension - whether called from REXX command files (.CMD), THE macros (.THE), or from other environments. It's also cumbersome to have to specify the extension when invoking the macro.
We measured 4 sub-cases:
[2a] MacroSpace function, pre-order, .CMD extension;
[2b] MacroSpace function, pre-order, no extension;
[2c] MacroSpace function, post-order, .CMD extension;
[2d] MacroSpace function, post-order, no extension;
 Leave the source code as a separate file, which is invoked anew for each call to the function. Although ideal in terms of maintenance, this approach will not produce good performance. In practice, disk caching will reduce the impact of all function calls after the first. Also, the performance will be affected by whether the source file is in the current directory, or how far the system has to search along the PATH string before it finds the source file. We therefore test these sub-categories:
[3a] source file in the current directory;
[3b] source file in a directory at the start of the PATH string.
[3c] source file in a directory at the end of the PATH string.
[3d] source file in a directory at the end of the PATH string, without EAs. (See below for the rationale for test [3d].)
Following are some measurements of elapsed time (in seconds) for 255 function calls using these various approaches (use the included test program to run this tests on your PC).
[C:\Usr\AFP\SW\Testing]REXX_Function_Call_Performance 255  function in the source program: 0.88 [2a] MacroSpace function, pre-order, .CMD extension: 2.06 [2b] MacroSpace function, pre-order, no extension: 2.13 [2c] MacroSpace function, post-order, .CMD extension: 91.09 [2d] MacroSpace function, post-order, no extension: 180.87 [3a] function in an external source file - CURRENT directory: 10.28 [3b] function in an external source file - START of PATH: 12.66 [3c] function in an external source file - END of PATH: 55.25 [3d] function in an external source file - END of PATH, no EAs: 42.12 [C:\Usr\AFP\SW\Testing]
1) The function used was:
REXX_Function_Call_Performance_1: return arg(1)**arg(1)
2) These measurements were on a 80486-DX4 with OS/2 Warp Connect (Blue Box) with no service applied, using (obviously) HPFS.
(Results on a P133 with 32 MB RAM and OS/2 WARP 4 with Fixpack #7 and Object REXX with HPFS:
D:\...\DEVELOP\REXX\FWTOOLS\REXXTT\Test>REXX_Function_Call_Performance 255  function in the source program: 0.15 [2a] MacroSpace function, pre-order, .CMD extension: 0.48 [2b] MacroSpace function, pre-order, no extension: 0.49 [2c] MacroSpace function, post-order, .CMD extension: 4.70 [2d] MacroSpace function, post-order, no extension: 11.77 [3a] function in an external source file - CURRENT directory: 2.12 [3b] function in an external source file - START of PATH: 2.53 [3c] function in an external source file - END of PATH: 5.82 [3d] function in an external source file - END of PATH, no EAs: 7.75
1. As expected, including the function in the primary source file was fastest.
2. Loading the function in the MacroSpace for pre-order execution is slower than including the function in the primary program, but much faster than invoking from a disk file.
3. Loading the function in the MacroSpace with an extension of .CMD is faster than loading without any extension, most notably when loaded for post-order execution, presumably because the first search is for macros with a .CMD extension. Note however that to achieve this performance, the extension must be specified both when the file is loaded and when the function is invoked - somewhat cumbersome, and probably of marginal benefit in the case of pre-order execution except in the most extreme cases.
4. When loading automatically from a disk file, the position of the function's source file in the PATH string can have a significant effect on performance - and the presence of network drives in the PATH string is likely to exacerbate the effect.
5. Loading in the MacroSpace for post-order execution is very slow. This slow performance can be partly explained because the system searches every directory in the search path (twice if the function is loaded without any extension), fails to find the function as an separate external file, and then finally looks in the MacroSpace (where it finds the function).
6. Even allowing for the preceding item, note that the performance of functions loaded in the MacroSpace for post-order execution is much slower in all cases than those loaded from disk (even when the source file's directory is at the end of the PATH string). Why is this? I thought that perhaps it was because the version loaded into the MacroSpace does not include the semi-compiled version which REXX normally stores in the Extended Attributes (EAs), but test [3d] shows that this alone does not explain the poor performance of tests [2c] and [2d] (The macro space contains only the tokenized code.)
7. Test [3d] (see above) strips the EAs from the function's source file and makes it read-only (which prevents REXX from attaching the semi-compiled form to the source file) to compare the performance of an external source file without the benefit of the semi-compiled form. Surprisingly, this version is faster than test [3c]! Unlikely though it seems, the semi-compiled form appears to be of no benefit in this test! Perhaps because the external function is quite small and relatively simple, the additional overhead of accessing and loading the EAs outweighs the benefit of the semi-compiled form? (I think the answer to this question is Yes. In addition, this is also dependent from the processor and harddisk used; see results for the P133 above.) In any case, the poor performance of tests [2c] and [2d] remains a puzzle.
8. Loading in the MacroSpace for pre-order execution therefore appears to be generally a good compromise - fairly good performance, easy to maintain, but has some management overheads.
1. Sent to THElist 1998-03-16
2. Updated copy sent to THElist 1998-08-03
Inf-HTML End Run - Successful