Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.
With v7r1, IBM improved the performance of RPG IV Subprocedures calls when the subprocedure includes a so called "return value". To leverage this performance improvement the RTNPARM keyword must be added to the prototype and procedure interface definition line. The prototype or procedure interface definition statement is normally where the "PR" and "PI" definition type is coded.
Basically numeric return values are fine and don't need to be considered as candidates for this keyword. However, large return values (greater than 16 bytes) can benefit from this keyword. Rather than use the extremely performance burdened STACK to return the value, the compiler simply converts the return value to a hidden first parameter, passed by reference. By doing this the overhead of the return value is completely eliminated.
For example, if you have a simple function named TOLOWER() that accepts a 64k input parameter and returns a 64k VARYING return value, the performance can be very poor. Obviously if you're calling TOLOWER() once, who cares? But let's look at the prototype and procedure for a TOLOWER function:
D toLower PR 65535A ExtProc('RPG_Convert_To_LowerCase') D Varying D szInput 65535A Const Varying P tolower B EXPORT D toLower PI 65535A Varying D szInput 65535A Const Varying D low C 'abcdefghijklmnopqrstuvwxyz' D up C 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' /free return %xlate(UP:low : szInput); /end-free P tolower E
When the TOLOWER function is used, you convert an input value to upper case and return it to the caller. The way the return value is set up (above) the returned value is placed on the stack. This causes a slow down as the system reformats the data for storage on the stack, then when it copies the value from the stack, it takes more time to reassemble it back into proper form, and then finally, copies it to the target variable. Here's a simple example:
D myName S 50A Inz('NEIL ARMSTRONG') /free myName = toLower( myName ); /end-free
When you're calling the procedure multiple times, the constant pushing onto the stack and removing the value from the stack can be time consuming. Since the stack is 16 bytes wide (or less) the returned value is broken up into piece and placed into 16-byte chunks on the Stack. Then the value is popped off the stack (in 16-byte chunks) and re-assembled into a temp/work variable, then finally it is copied to the target value.
By adding the RTNPARM keyword you completely remove the interaction with the stack. The value is instead passed as a hidden 1st parameter by the compiler; shifting the remaining parameters down. Improved performance is accomplished by avoiding the stack and hiding the return value as the first parameter of the call. Here's what the modified code look like:
D toLower PI 65535A ExtProc('RPG_Convert_To_LowerCase') D Varying RTNPARM D szInput 65535A Const Varying P tolower B EXPORT D toLower PI 65535A Varying RTNPARM D szInput 65535A Const Varying D low C 'abcdefghijklmnopqrstuvwxyz' D up C 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' /free return %xlate(UP : low : szInput); /end-free P tolower E
Now when the TOLOWER function is used, performance is substantially improved over the previous method.
Because RTNPARM causes the returned value to be passed as a hidden parameter, your function has to be aware of this. For example, if you have parameter numbers in your function body, for example used with %PARMS() or one of the CEE APIs such as CEEDOD or CEESTA, you will need to adjust that number when RTNPARM is used. While the parameter is hidden, IBM did not adjust other portions of the compiler to accommodate RTNPARM. Therefore YOU HAVE TO UP THE PARAMETER NUMBERS by 1 when RTNPARM is used. If you avoid doing this you'll certainly have a learning experience.