Large Subprocedure Return Values: V7 Brings Relief
November 17, 2010 Jon Paris
In Subprocedure Return Values–Food for Thought, I discussed the performance implications of returning large variables from subprocedures. With IBM i 7.1, IBM has added new RPG compiler features that improve performance when passing large parameters.
The conclusion of my previous tip was that you should use a conventional parameter–instead of returning a value–when large values such as result sets were involved. The downside of such a change of course is that you lose the ability to know exactly which field was changed by a procedure call. Seeing:
GetCustomers( state: customerList );
in the code just doesn’t make what is happening as obvious as:
customerList = GetCustomers( state );
V7 of RPG IV saves you from having to make such efficiency vs. design tradeoffs by providing a mechanism for automatically making the switch from return value to parameter “under the covers.” Below is the original “large return value” prototype that I used in the previous tip, followed by the “new and improved” V7 version:
D TestProc1 Pr 32000a D TestProcV7 Pr 32000a RTNPARM
As you can see, other than the name, the only difference is the addition of the RTNPARM keyword. As you would expect, this keyword must also be coded on the procedure interface.
When the keyword is used, RPG passes an additional parameter under the covers to act as the return value. The result is that our test case now runs much faster. It’s still not as fast as the less intuitive method of passing an extra parameter, but significantly faster than the original.
So far so good, but (there always has to be a “but”) there are some additional considerations that you need to be aware of.
First of all, since an extra parameter is actually being passed, use of %PARMS() in the called procedure will return a value one greater than the number of declared parameters. Just to be clear about this, in the case above, a call to %PARMS() in TestProc1 would return the value 0, whereas in TestProcV7 it would return the value 1. This is obviously significant in cases where %PARMS() is being used to determine the presence or absence of optional parameters, or when a parameter number must be specified, such as when calling APIs such as CEEDOD or CEETSTA.
Luckily for all of us, the RPG developers recognized the potential problems that this could cause and have provided us with a better and safer way to deal with parameter numbers. The solution is the new BIF %PARMNUM(). This BIF takes as its parameter the name of an input parameter and returns an integer that specifies that parameter’s position in the list. This value can safely be used when calling CEEDOD or CEETSTA since it will have been automatically adjusted when necessary to accommodate the effect of RTNPARM.
Those of you who make use of optional parameters (i.e. OPTIONS(*NOPASS)) will also find this new BIF useful. No longer do you need to hard-code your tests for optional parms: You can now soft-code them, which is a much safer approach that will ensure future changes to your parameter list are less likely to cause problems.
Here’s a simple comparison of the traditional and the new approach:
D MyProc PI LikeDS(myResult) D Compulsory 20a D Optional 10a Options(*NoPass) /Free // The old way of testing if field Optional was passed If %Parms > 1; DoStuff(); EndIf; // and the new safer approach If %ParmNum(Optional) <= %Parms; DoStuff(); EndIf;
As the example stands, both will work. But now imagine that you decide to return a result set rather than a single entry. Knowing that the return value will now be much larger, you might choose to add the RTNPARM keyword to the PI like so:
D MyProc PI LikeDS(myResult) D Dim(999) D RTNPARM
If you had used the original approach, would you have remembered to also make the change to the %Parms test? I know I wouldn’t always remember to change the code to test for greater than 2 instead of 1. However, if I implement the V7 approach and use %ParmNum() then I don’t have to worry–everything will still work automagically!
One last point before I close. Remember that if you need to call an RPG program that uses RTNPARM from another language (such as COBOL, C, or CL) then it is up to you to ensure that you handle the extra parameter correctly. By “correctly” I mean that you must pass an additional parameter as the first parameter to the subprocedure–and of course make sure it is at least as large as the RPG program defines it to be. Perhaps one day IBM will update the other languages to provide similar functionality, but for now you have to manage it yourself.
Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summit conference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page.
Subprocedure Return Values–Food for Thought