fhg
Volume 7, Number 23 -- June 20, 2007

Parameter Passing and Performance

Published: June 20, 2007

by Ted Holt

There are three ways to pass a parameter to a procedure: by reference, by value, and by read-only reference. These methods are not interchangeable, and passing parameters by value can have unfavorable effects on performance. In the following paragraphs, I explain why I make such a statement, I show you how to define parameters for performance, I list the performance figures from my testing, and I provide some recommendations for parameter passing.

Before I go any further, let me get a couple of things out of the way. First, keep in mind that I am talking about you and the routines you write. If you call someone else's routine, you have to follow whatever convention they used. If they decided that all the parameters were to be passed by value, you'll have to pass them by value as well, no matter how you feel about it.

Second, I thank Barbara Morris of the RPG compiler team for answering questions I put to her regarding the passing of parameters. Any erroneous conclusions I may have reached are my own fault, of course.

Let's say you've decided to write a subprocedure to be used in ILE programs. Language doesn't matter, so I'm going to use RPG for the examples. You name your subprocedure DoIt, and it requires one parameter to do its job. Which method are you going to use to pass the parameter to the subprocedure?

By reference?

P DoIt            b                                
D                 pi                               
D  Type                          2a

By value?

P DoIt            b                                
D                 pi                               
D  Type                          2a   value

By read-only reference?

P DoIt            b                                
D                 pi                               
D  Type                          2a   const

First, let's consider passing by value. The VALUE keyword on the parameter definition means that a caller routine makes a copy of the data that it wants DoIt to accept as a parameter. DoIt operates on the copy, rather than on the data itself. Look at the following example.

H dftactgrp(*no) actgrp(*new)               
                                            
D ItemTypeCode    s              2a         
                                            
D DoIt            pr                        
D  Type                          2a   value 
 /free                                      
     *inlr = *on;                           
     DoIt (ItemTypeCode);                   
     return;                                
 /end-free                                  
 * =========================================
P DoIt            b                         
D                 pi                        
D  Type                          2a   value 
                                            
 /free                                      
     // do some stuff                       
     return;                                
 /end-free                                  
P                 e                         

The main routine invokes DoIt, passing a copy of ItemTypeCode in the first (and only) parameter. DoIt refers to the copy of ItemTypeCode as Type. If DoIt changes Type, the change occurs to the copy of ItemTypeCode, not to ItemTypeCode itself. (I do not like this ability to change parameters that are passed by value. I see assignments to such parameters as misleading, since it appears that a data value in the caller is changed, when in fact it is not changed. But this preference of mine is not applicable to the question of performance that I am attempting to address.)

Next, let's consider read-only reference. Here's the same example.

H dftactgrp(*no) actgrp(*new)               
                                            
D ItemTypeCode    s              2a         
                                            
D DoIt            pr                        
D  Type                          2a   const
 /free                                      
     *inlr = *on;                           
     DoIt (ItemTypeCode);                   
     return;                                
 /end-free                                  
 * =========================================
P DoIt            b                         
D                 pi                        
D  Type                          2a   const 
                                            
 /free                                      
     // do some stuff                       
     return;                                
 /end-free                                  
P                 e                         

The CONST keyword tells the caller to pass the address of ItemTypeCode to DoIt. However, DoIt is not allowed to modify the data it refers to as Type.

Obviously this method doesn't work if the subprocedure must change the parameter. In that case, you'd omit both VALUE and CONST keywords, which would be passing the parameter by reference.

H dftactgrp(*no) actgrp(*new)               
                                            
D ItemTypeCode    s              2a         
                                            
D DoIt            pr                        
D  Type                          2a
 /free                                      
     *inlr = *on;                           
     DoIt (ItemTypeCode);                   
     return;                                
 /end-free                                  
 * =========================================
P DoIt            b                         
D                 pi                        
D  Type                          2a
                                            
 /free                                      
     // do some stuff                       
     return;                                
 /end-free                                  
P                 e                         

The main routine provides DoIt with a pointer to (i.e., the memory address of) ItemTypeCode. Anything that DoIt does to Type is really done to ItemTypeCode.

The first thing to consider, then, is whether or not the parameter is to be modified. If a subprocedure is to be able to modify the parameter, you must pass the parameter by reference.

But if a subprocedure does not modify a parameter, it's better to pass the parameter by value or by read-only reference. These last two parameter-passing mechanisms have two advantages over passing by reference.

  1. When you pass a parameter by reference, that parameter must be defined identically in the calling routine and subprocedure. When you pass a parameter by value or read-only reference, parameters in the calling routine and called routine do not have be defined identically, although they must be defined in compatible ways. For instance, it's OK for a caller to pass a three-digit packed-decimal number to a subprocedure that expects a seven-digit packed-decimal number.
  2. When you pass a parameter by reference, the caller must pass a variable. Passing a parameter by value or read-only reference allows the caller to pass literals and expressions to the subprocedure.

Does it matter, then, whether I pass parameters by value or pass by read-only reference? From a performance standpoint, I knew it must. After all, the system can allocate memory for a pointer much more quickly than it can allocate memory for a large character variable. I ran a few tests to get an idea of how the passing of parameters affects performance. The times given in the following tables are CPU seconds, as reported in the job log. This is hardly scientific, but plenty good enough for our purposes.

In the first version of my test program, I passed a 64-byte variable-length character to a subprocedure. I repeatedly invoked the subprocedure within a loop.

H dftactgrp(*no) actgrp(*new)

D BigString       s             24a   varying
D                                     inz('I like cheese.')
D Index           s             10u 0
D Limit           s             10u 0
D                                     inz(500000)

D DoIt            pr
D  inString                     64a   varying const
 /free
     *inlr = *on;
     for Index = 1 to Limit;
        DoIt (BigString);
     endfor;
     return;
 /end-free
 * =============================================================
P DoIt            b
D                 pi
D  inString                     64a   varying const
  /free
     return;
 /end-free
P                 e

The following table shows execution times for various numbers of iterations.


Number of Iterations

Run time, CONST

Run time, VALUE

500,000

1

1

1,000,000

1

1

2,500,000

1

1

5,000,000

1

1

7,500,000

1

1

Table 1: Invoking a subprocedure that accepts a 64-byte variable-length character parameter.


Does it seem to make any difference which method you use?

I changed the parameter length from 64 bytes to 64 kilobytes (65535 bytes) and reran the tests. The next table looks a bit different from the previous one.


Number of Iterations

Run time, CONST

Run time, VALUE

500,000

1

22

7,500,000

1

282

Table 2: Invoking a subprocedure that accepts a 64 kilobyte variable-length character parameter.


Hmmm, passing by value doesn't look so good anymore, does it? Think about what's happening. Each time the caller invokes the subprocedure, it must first allocate memory for the parameter. When passing by read-only reference, it must allocate enough memory for a pointer, which is a matter of bytes. But when passing by value, it must allocate 64 kilobytes. Upon return to the caller, the system deallocates the memory. Allocating and deallocating 64 bytes is no big deal, but allocating and deallocating 64 kilobytes is.

I tried another test. This time, I passed an expression, rather than a scalar variable, to the subprocedure. In this case, the system has to do a bit more work before calling the subprocedure. Instead of passing a pointer to a variable, the system must evaluate the expression.

H dftactgrp(*no) actgrp(*new)

D BigString       s          65535a   varying
D                                     inz('I like cheese.')
D Index           s             10u 0
D Limit           s             10u 0
D                                     inz(500000)
D DoIt            pr
D  inString                  65535a   varying const
 /free
     *inlr = *on;
     %len(BigString) = 65535;
     for Index = 1 to Limit;
        DoIt (%subst(BigString:32768:32768)+%subst(BigString:1:32767));
     endfor;
     return;
 /end-free
 * =============================================================
P DoIt            b
D                 pi
D  inString                  65535a   varying const
  /free
     return;
  /end-free
P                 e 

The next table shows how passing by read-only reference and passing by value handled the extra overhead.


Number of Iterations

Run time, CONST

Run time, VALUE

500,000

10

28

7,500,000

147

427

Table 3: Invoking a subprocedure, passing a 64-kilobyte expression as the first parameter.


Runtime went up--way up. However, it went up much more when the parameter was passed by value.

What did I learn from my tests?

  1. The size of the parameter is important. It doesn't matter whether you pass small values, such as integers, by value or by address. I'm not sure what data types we should consider to be small values. Integer, unsigned integer, floating point, packed-decimal, zoned-decimal, date, time, timestamp, and indicator variables are all small, so either method should work for them. The same should apply to short character variables. I don't have any scientific basis for saying this, but I don't think I'd pass a character variable longer than 1096 bytes by value. I also don't think I will henceforth automatically make all variable-length character parameters 64 kilobytes long, just in case I ever need to operate on a really big string. I'm reminded of what my friend Cletus the Codeslinger tells his kids, "The world is full of morons. Try not to be one of them."
  2. Consider the frequency of the call. If a subprocedure is called only once in a program, performance is probably not critical. However, if a subprocedure is called once for each record of a 350,000-record file, passing parameters by value may very well slow the job noticeably.
  3. I see a strong case for avoiding pass-by-value completely. Read-only reference provides the same benefits as pass-by-value with a smaller performance penalty.

There is more to this performance issue than what I have covered here. I will get back to you with more information, maybe even by next week.



                     Post this story to del.icio.us
               Post this story to Digg
    Post this story to Slashdot


Sponsored By
HELP/SYSTEMS

SEQUEL can be used for
virtually ALL business intelligence functions
on the System i, including:

                                                    · Executive Dashboards
                                                    · Graphical Query & Reporting
                                                    · Drill-Down Data Analysis
                                                    · Multi-Platform Database Support
                                                    · E-Mail Report and File Distribution
                                                    · Secure Web Access

SEQUEL is the single solution for all
your business intelligence needs.

www.helpsystems.com


Senior Technical Editor: Ted Holt
Technical Editors: Howard Arner, Joe Hertvik, Shannon O'Donnell, Kevin Vandever
Contributing Technical Editors: Joel Cochran, Wayne O. Evans, Raymond Everhart,
Bruce Guetzkow, Brian Kelly, Marc Logemann, David Morris
Publisher and Advertising Director: Jenny Thomas
Advertising Sales Representative: Kim Reed
Contact the Editors: To contact anyone on the IT Jungle Team
Go to our contacts page and send us a message.

Sponsored Links

COMMON:  Join us at the Annual 2008 conference, March 30 - April 3, in Nashville, Tennessee
BCD:  The best integrated System i Modernization software from one vendor
Krengeltech:  Create XML Web Services from RPG - without Java or Websphere


IT Jungle Store Top Book Picks

The System i Pocket RPG & RPG IV Guide: List Price, $69.95
The iSeries Pocket Database Guide: List Price, $59.00
The iSeries Pocket Developers' Guide: List Price, $59.00
The iSeries Pocket SQL Guide: List Price, $59.00
The iSeries Pocket Query Guide: List Price, $49.00
The iSeries Pocket WebFacing Primer: List Price, $39.00
Migrating to WebSphere Express for iSeries: List Price, $49.00
iSeries Express Web Implementer's Guide: List Price, $59.00
Getting Started with WebSphere Development Studio for iSeries: List Price, $79.95
Getting Started With WebSphere Development Studio Client for iSeries: List Price, $89.00
Getting Started with WebSphere Express for iSeries: List Price, $49.00
WebFacing Application Design and Development Guide: List Price, $55.00
Can the AS/400 Survive IBM?: List Price, $49.00
The All-Everything Machine: List Price, $29.95
Chip Wars: List Price, $29.95

 

The Four Hundred
The i5 515 and 525 Versus the Unix Competition

Vision Solutions Acquires HA Rival Lakeview Technology

Adoption of VoIP Tied to Relief from Phone Expenses

The Web Runtime Tax: The Tax Man Cometh, Again

The Linux Beacon
Linspire Hooks Up with Microsoft, Too

Intel Bangs the Itanium Drum, Draws Out Roadmap

Novell Ships Service Pack 1 for SUSE Linux 10

Torvalds Says Linux May Follow Solaris with GPL v3

Four Hundred Stuff
IBM Taps Nortel for Entry-Level System i VoIP Solution

North Carolina Schools Laud SafeData for Online DR Solution

NGS Hooks Into Query/400 to Protect BI Investments

S4i Expands File Support in Document Management Software

Big Iron
IBM Brings Freebie PHP to the Mainframe

Top Mainframe Stories From Around the Web

Chats, Webinars, Seminars, Shows, and Other Happenings

System i PTF Guide
June 16, 2007: Volume 9, Number 24

June 9, 2007: Volume 9, Number 23

June 2, 2007: Volume 9, Number 22

May 26, 2007: Volume 9, Number 21

May 19, 2007: Volume 9, Number 20

May 12, 2007: Volume 9, Number 19

The Windows Observer
Microsoft Patches 17 Flaws in Client Products

Microsoft Stretches 'Vision Thing' with Surface Computing

Microsoft Updates Server Virtualization Software

Sun Broadens Its Blade Server Lineup

The Unix Guardian
Apple Previews Mac OS X 10.5 'Leopard' Server

CIOs Get Ready to Hire in the Summer

Open Source Software Sales Pegged at $5.8 Billion by 2011

As I See It: The Ne'er-Do-Well's Guide to Enlightenment

Four Hundred Monitor
Four Hundred Monitor's
Full iSeries Events Calendar

THIS ISSUE SPONSORED BY:

Help/Systems
WorksRight Software
Guild Companies



TABLE OF CONTENTS
Parameter Passing and Performance

Conditional Counting with Open Query File

What Is SMIOSTCPGT and Why Is It Eating My System?

Four Hundred Guru

BACK ISSUES

From the IT Jungle Forums
Stuck in the Dark Ages

Referencing the key of current record

FNDSTRPDM Output Member Name to *OUTFILE

Brother HL 6050 printing '$' instead of '£' symbol

XML





 
Subscription Information:
You can unsubscribe, change your email address, or sign up for any of IT Jungle's free e-newsletters through our Web site at http://www.itjungle.com/sub/subscribe.html.

Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc., 50 Park Terrace East, Suite 8F, New York, NY 10034

Privacy Statement