I recently did an unscientific performance test comparing MOVEL, EVAL, the C runtime function named memcpy, and an MI instruction named CPYBYTES. I was surprised by the results. So let's review those results and look at the basic purpose of each of these functions.
MOVEL Opcode
The MOVEL and MOVE opcodes are always converted to the CPYBLA or CPYBRA MI instructions for character data copying. If the P (pad) operation extender is used, then the opcodes are converted to CPYBLAP and CPYBRAP respectively. The CPYBLAx and CPYBRAx instructions have been around for decades and perform very well.
The MOVEL opcode sometimes generates additional code that copies its value to a work field and then copies that value to the target. This is rare, but it can happen.
You can call CPYBLAx or CPYBRAx directly by prototyping the MI instruction. In RPG IV, the prototypes would appear as follows:
D pTarget * Value
D pSource * Value
D nSrcLen 10I 0 Value
D cpyblap PR ExtProc('cpyblap')
D pTarget * Value
D nTgtLen 10I 0 Value
D pSource * Value
D nSrcLen 10I 0 Value
D cPadChar 1A Value
It is obviously more complicated to prototype and call these MI instructions than it is to simply use the MOVEL opcode, but you do gain a very slight performance advantage.
EVAL Opcode
Probably one of the most surprising results of my performance test was that EVAL is slower than most other methods of copying data. This is a major disappointment. You would think that IBM would have optimized EVAL, considering that the company advocates free-format syntax as well as the hybrid syntax most people use. In both cases, EVAL is the foundation for copying character data and should be the most optimized of any operation.
Since RPG IV is a new compiler, I can't simply dump the IRP (MI instruction stream) listing and see what code EVAL generates. I can't read the new MI used for ILE, so I don't know what the issue is with EVAL. Granted, it isn't a show-stopper, and I will continue to use it, but in performance-critical situations, I may choose other methods.
This performance issue with EVAL may have originated with the original programmer of that opcode (who left the RPG IV team before RPG IV was announced), or it may have been code from the extremely poorly designed CAT, SUBST, and XLATE opcodes introduced back in RPG III. Originally, those opcodes were so poorly implemented that I spent hours on the phone with IBM trying to get them to change things. Eventually, IBM did correct the problem, and the flaws in that original code have been worked out. But I worry that, because something so poorly designed made it into the compiler, maybe the same thing happened with EVAL.
C Function: memcpy()
The memcpy() function is actually pretty cool once you get used to using it. When I learned C some 15+ years ago, I thought it was the C function that was most like the RPG MOVEL opcode.
The memcpy() function copies data from one field to another. It uses pointers to identify the source and target locations, giving you the ability to use it like a substring operation or a straight copy function. When I run out of room on an RPG IV statement because of the length of the %SUBST, I'll often use memcpy(). Consider this line of code, for example:
That can often be replaced with this:
However, I might do this instead:
Eval pSource = %addr(source)+7
callp memcpy(pTarget: pSource : 5)
The prototype for memcpy() follows. Note that there are two underscore characters (_) at the beginning of the function name in the EXTPROC keyword:
D pTarget * Value
D pSource * Value
D nLength 10U 0 Value
CPYBYTES MI Instruction
The final method to copy character data that I investigated was the CPYBYTES MI instruction. This instruction is supposed to run twice as fast as memcpy() or CPYBLAx. I recently learned of this instruction from a long-time friend from IBM Rochester (now retired) who is probably the smartest person I know. In fact, they've banned him from Las Vegas casinos because he can count cards seemingly without end.
In any event, he said that CPYBYTES runs two times as fast as memcpy because it does not copy data that contains pointers. This gives the optimizer a bit of an advantage as it doesn't have to do the normal things it would do when handling data that may contain pointers.
Virtually none of the data in RPG programs contains pointers--or rather, none that is copied normally contains pointers. So CPYBYTES can, generally speaking, be used anytime RPG IV applications need better performance.
Calling CPYBYTES is about the same as calling memcpy(): You specify the address of the target, followed by the address of the source, followed by the number of bytes to copy. Here is the prototype:
D pTarget * Value
D pSource * Value
D nLength 10U 0 Value
I use memcpy() or CPYBYTES to copy large variables (several thousand characters in length) or repeatedly copy data over and over (thousands of copies). In the RPG xTools, I use memcpy() in the CPYUSRSPACE (Copy User Space Data) procedure because user space may contain pointers; therefore, CPYBYTES was not a viable option.
So give these alternatives to MOVEL and EVAL a try if you have a performance-critical situation or just want to have fun.
To download the prototypes from this article, visit the RPGLab, where you can find a set of free source code, including all the MI and C prototypes featured in this article. It's hosted free on the RPGIV.com Web site.
Bob Cozzi is a programmer/consultant, writer/author, and software developer. His popular RPG xTools add-on subprocedure library for RPG IV is fast becoming a standard with RPG developers. His book The Modern RPG Language has been the most widely used RPG programming book for more than a decade. He, along with others, speaks at and produces the highly popular RPG World conference for RPG programmers.
LATEST COMMENTS
MC Press Online