Subroutines vs. Functions/Procedures

Both SBXs and user-defined functions and procedures—hereafter referred to collectively as functions—provide the ability to create reusable, parameterized code modules, giving rise to the question of which approach is better. As with most questions of that type, the answer depends on a variety of considerations, including personal preference. The following factors and comments may help you decide which is better for your situations.

Re-usability

The most significant different between the two is that SBXs are entirely separate modules, essentially separate programs, loaded on demand—see Subroutines vs. Programs—while functions and procedures are compiled directly into a program. Re-usability of SBXs comes from the fact that once compiled and placed in the search path of the directory system, they are available for use by any program at run time. SBXs can be compiled and then distributed to other systems, even while there are active users potentially using those SBXs, and they will automatically detect and use the new version on the next call. Re-usability of functions comes requires using the ++INCLUDE statement to load them into your program at compile time, so if you need to update a function and distribute it, you'll need to recompile all the programs using that function and redistribute all those programs. So SBXs have a big edge here in flexibility.

Performance

Although SBXs are cached in memory, greatly reducing disk overhead for repetitive calls, there is still a lot more CPU and memory overhead in the calling process, compared to functions. As an example, a comparison test on an i7 Core laptop of a simple routine with several parameters, coded both as an SBX and as a function, yielded about 10 microseconds per SBX call, vs. about 1.5 microseconds for each function call. That's a difference of about seven times, but it may not be meaningful unless you need to call the routine hundreds of thousands of times.

Memory Overhead

Each SBX execution instance requires it's own memory partition—see the System Parameter MEMORY—which typically needs to be at least about 500K. In comparison, each instance of a Function call needs only enough memory for the parameters and stack, typically less than 5K. So that's a huge (~100X) difference, but with the amount of memory on modern systems, perhaps not that significant either.

Parameter Passing

Both SBXs and functions have similar parameter passing mechanisms, and in fact use the same X-Args statements within the called routine.

Global Variables

SBXs are not able to share any global variables with the calling program, except through external means such as files, common memory modules, parameters, etc. Functions, on the other hand, are able to share any variables with the rest of the program. However, this isn't really much of an advantage, since the use of global variables undermines the ability of either type of routine to encapsulate its internals and separate itself from the rest of your logic.

Persistent Variables

Functions have a big advantage here in their ability to declare STATIC local and PRIVATE module-level variables which persist across calls. SBXs have no such capability except through external storage.

File Handling

SBXs and Functions are roughly the same in the fact that they share the file table with the calling program. Any files opened by the calling program are accessible, assuming the file channel is made available to the routine, and any files opened within the routine remain open on return, unless explicitly closed. SBXs do support a global ASFLAG option to adjust that behavior, possibly giving them an edge in flexibility.

Remote Execution on ATE Client

SBXs, being independent files loaded at runtime, have the ability to be transferred at runtime to the ATE client for remote execution, a trick which Functions cannot duplicate. See the module FNATESBX.BSI in SOSLIB:[907,10] for an easy way to facilitate such remote calls.

Recursion and Nesting

Both SBXs and functions support recursion and heterogeneous nesting—mixtures of nested SBX and function calls recursive and otherwise—but the memory overhead is significantly lower in the function case, making deep nesting more practical.