Performance of Function Subprocedures
June 27, 2007 Ted Holt
Note: The code accompanying this article is available for download here.
Last week, I wrote about performance issues when parameters are passed to subprocedures. This week, I write about a similar matter. If a subprocedure returns a value to the caller (i.e., the subprocedure serves as a function), the computer has to store the returned value somewhere in memory. The fact that the system must allocate memory for returned values means that performance may be affected.
To test the performance of function subprocedures, I wrote two substring functions. The first one, SubString, works like the %SUBST built-in function that IBM gives us as part of the RPG compiler, but it differs in two ways:
Here’s a version of SubString that works with 64-byte strings:
P SubString b D pi 64a varying D inString 64a varying const D inPosition 10i 0 const D inLength 10i 0 const D D EmptyString c const('') /free if inPosition <= *zero or inLength <= *zero or inPosition > %len(inString); return EmptyString; endif; if (inPosition + inLength - 1) > %len(inString); return %subst(inString: inPosition); endif; return %subst(inString: inPosition: inLength); /end-free P e
Notice that all three parameters are passed by read-only reference, so there is no parameter overhead of any significance. For those of you who are still fuzzy about subprocedures, the second line is the one that defines the return value.
D pi 64a varying
The second subprocedure, SubStringThru, differs from %SUBST in the definition of the third parameter. In both functions, the first parameter is the string from which the substring is to be derived, and the second parameter is the beginning position. However, SubStringThru’s third parameter is the ending position of the substring, not the length of the returned string. For example, SubStringThru (SomeString: 4: 10) means “the characters in positions four through ten of SomeString”, which is equivalent to %SUBST(SomeString: 4: 7).
I wrote two versions of SubStringThru. Version 1 of SubStringThru uses a modular programming technique my professors pounded into my head when I was getting my computer science degree–build new routines from existing routines, otherwise known as “don’t reinvent the wheel.” Version 1 uses the SubString subprocedure to do its grunt work. Here is the subprocedure defined to return a 64-byte value.
P SubStringThru b D pi 64a varying D inString 64a varying const D inBeginPos 10i 0 const D inEndPos 10i 0 const D D Length s 10i 0 D EmptyString c const('') /free Length = inEndPos - inBeginPos + 1; return SubString(inString: inBeginPos: Length); /end-free P e
Version 2 does its own grunt work.
P SubStringThru b D pi 64a varying D inString 64a varying const D inBeginPos 10i 0 const D inEndPos 10i 0 const D D Length s 10i 0 D EmptyString c const('') /free Length = inEndPos - inBeginPos + 1; if inBeginPos <= *zero or Length <= *zero or inBeginPos > %len(inString); return EmptyString; endif; if (inBeginPos + Length - 1) > %len(inString); return %subst(inString: inBeginPos); endif; return %subst(inString: inBeginPos: Length); /end-free P e
Now for the tests. I used the same method I used in the previous article. That is, rather than use some mechanism that measures in fractions of seconds (e.g., PEX), I chose to measure performance in CPU seconds. I chose this method because I believe it gives a truer picture of how subprocedures perform in production. I’ve included the code in a zip file, in case you’d like to do your own testing.
The first performance test consisted of 500,000 calls to the various substring techniques. I used two lengths of return values–64 bytes and 64-kilobytes. Take a look at this table.
The numbers indicate elapsed CPU seconds. Here are the same figures for 2,500,000 calls.
The last two lines of the table tell why I wrote the second version of SubStringThru. The first version performed terribly because each invocation generated a second subprocedure invocation, an invocation of SubString.
The lessons I learned from these tests are similar to those I shared with you in the previous article.
I still like subprocedures and recommend that developers use them. But I have decided that I will try to be more judicious about the way I implement them.
I have yet more to cover concerning performance and subprocedures. Maybe next week or the week after.