| There's an old story that appears in Weinberg's "Psuchology of Computer
Programming" to the effect that if you don't care whether or not the
program produces good results, you can make it run as fast as you want.
Whether to use function calls boils down to what functions you need to
perform and whether you want to hand code them or use existing subroutine
libraries. If you need a function to be evaluated at several places then
you either put the code in-line several times, or you use function calls.
In the first case you pay for additional disk transfer time to load the
extra code in the program; in the second you pay for the function call.
The first method is also notoriously risky - did you do it exactly the same
way every time? Will the cost of recompiling your code several additional
times, measured in minutes, be repaid by faster execution speed measured in
milliseconds?
At any rate, the call instruction compiled by C is no more or less expensive
than the call instruction compiled by FORTRAN or MACRO. What may be costly
is a poor compiler that generates inefficient register-handling code, and
there are several machines for which the C compilers are worse than the VAX
C compiler.
Lynn Yarbrough
|
| Depending on the machine and compiler, sometimes a function call
may be more efficient, because it makes more registers available
to the routine that is making the function call, and thus may be
able to put variables in registers rather than on the stack. Especially
when the body of the function is rather complex, the call overhead
gets proprotionally lower. And, inlining large functions can (but
not always) really hurt.
Personally, I like the compiler to "do the right thing", which is
what VAX Ada does with its auto-inlining. If it looks like it will
save time and space, its inlined, if not, we call it. Depending
on the functions arguments, in some cases it may be inlined, and
in other cases called (say if one of the actuals is a CTC, you may
be able to optimize away a lot of code that you couldn't if you
called it). Had to put in a plug for Ada. PASCAL autoinlines too,
I think.
|
| Re .1:
> At any rate, the call instruction compiled by C is no more or less expensive
> than the call instruction compiled by FORTRAN or MACRO.
Yes, that is largely true. However, just as you must be concerned
about passing large "value" parameters in Pascal, you should be
concerned about the same in C, if you have a genuine need for souped-up
performance. In addition, while you can consider the calling overhead
associated with most languages to be *roughly* equivalent, there
*does* exist extra semantic baggage which must be carried by procedure
call code for higher-level languages. The copying of C or Pascal
"value" parameters falls into this category. The effect is usually
small (except in pathological cases, such as passing large arrays by
value), but it does exist, and different languages have different kinds
of baggage which must be carried.
The tradeoff between compilation time and execution time truly
depends upon the requirements levied against your application (must
it perform some complex system function in milliseconds) and the
relative number of times it is compiled versus executed. However,
the more interesting tradeoff is related to code maintainability
versus efficiency.
If I use the same function in a dozen places, then if I were to
"inline" all of these uses, I would have a dozen copies (and after
normal maintenance, probably a dozen versions) of code which allegedly
perform the same function. Every time I "fix" one of these copies,
I should fix all of them, but I'll forget one of them, or I'll make
a mistake when I "fix" one of them, and so on.
Functions (procedures) are a nice thing; don't throw away the tool
unless you absolutely must. Code up your application using functions.
Use VAX Performance and Coverage Analyzer (PCA) if you are using
VAX/VMS, or the appropriate tool under some other operating system,
to identify the "hot spots" in your program, that can most benefit
from hand-optimization.
Then, if you find yourself spending a lot of time making function calls
in a tight loop and the calls are not recursive, you may want to
"optimize" those calls. If the function(s) is(are) used only in this
location, feel free to insert the text by hand. If used in several
places, you have a couple options in C (Pascal and Ada can give you
true inlining):
o Use a preprocessor macro. As long as the function body is
reasonably small (can fit on a "single line", which is an
implementation-dependent notion), you can define a macro (with
parameters) which will expand to that sequence of statements.
You can even define a block ({decl... statement...}) as long
as it fits. Notice, however, that the parameters are *textual
substitutions* rather than actual parameters; the semantic
baggage is different.
o If the function body is too large for that, you can put the
body (preferably a block with local variables) in an #include
file, and refer to agreed-upon objects for inputs and outputs.
You lose the parameterization of a function call or a macro
invocation, but you still get "inline" code without having multiple
copies to maintain.
Depending upon the ability of the compiler's optimizer/register
allocator to deal with the increase function size, such inlining
will also allow "global optimization" of the "function" and its
callers, which may result in further performance improvements.
However, don't do any of this if you simply want "fast code". These
approaches are definitely inferior (from a software quality standpoint)
to genuine function/procedure call semantics; use them only if
you *must* and only *when* and *where* you must to improve lackluster
performance.
Lloyd
|
| Perhaps the following is so obvious that it doesn't need to be said,
but I'm going to say it anyway.
The biggest performance payoffs usually come from choosing a different
algorithm. Except in pathological cases, no amount of in-lining or
other local optimizations will make an exponential algorithm run
faster than a linear one, for large quantities of data.
Performance is just one property of a program. Maintainability is
another. As .3 points out, you always need to ask whether a given
performance improvement is worth any associated maintenance costs.
Less obviously, you should also be asking whether you should be
spending your time trying to improve a given program by improving its
performance, or by improving its maintainability. We tend to emphasize
the former, because it's easier to measure performance improvements,
and because customers get to see those improvements directly. We only
get to see maintenance costs when you lose a week tracking an obscure
bug that would never have occurred if the original author of the code
had spent a day improving the documentation, instead of spending a day
squeezing an extra microsecond out of the code.
Gary
|
| VAX Pascal does do automatic inlining based on a guess about the
"rightness" of doing so at each call point. Performance measurements
I did indicated a run-time speed-up of about 20% due to inlining
(when combined with constant value propagation and expression folding,
so the experience with C may be different).
Gary Feldman is right--first, choose an algorithm with good time
complexity. Next, write code which is clear and well documented,
with a "big picture" discussion somewhere obvious. _Then_ worry
about picking up the last few percent by inlining and so on.
-John Bishop
|