[comp.lang.forth] Procedure call overhead

wmb@MITCH.ENG.SUN.COM (03/22/90)

> I may regret asking this, but here goes.  I notice that in
> many comp.lang.forth postings there is a distinct tendency
> to criticise other languages because of procedure call
> overhead.  Now I would be the first to agree that overhead
> is to be avoided where possible, but is it not taking things
> a little too far to say C is too slow?  If that's how you
> feel about C, what do you have to say about Pascal, or Ada?

I think a lot of Forth programmers have this "urban myth" notion
that C procedure calls are horribly slow and the Forth procedure
calls are blazing fast.

It ain't so.

On most RISC processors, procedure calls are blazing fast.  Even
on CISC processors, they aren't slow.  You have do do some pushing
and popping, but not an execessive amount.  In most applications,
procedure call overhead rarely amounts to a significant overall cost.

People seem to use efficiency arguments to justify the writing of long
procedures.  In most cases, I believe those arguments are specious.


I believe that the *syntactic* overhead of *coding* C procedure calls is
the most significant impediment to factoring C procedures into small
chunks.  To create a new C procedure, you need to declare variables
and stuff; this is just enough trouble to keep a lot of people from
doing it routinely.  The syntactic overhead of a typical 2-input,
1-output procedure is:

 int
 doit(foo,bar)
  int foo;
  int bar;
 {
  <declare local variables>;

  return(<result>);

 }

This is even worse if instead of "int"s, you want the arguments to be
of type "register struct mumble *".

In Forth, you would instead have:

 : doit  ( foo bar -- result )
 ;

Admittedly, people *ought* to be willing to pay the syntactic price
(i.e. go to the effort) of creating a new procedure, but they usually
aren't, especially when they can rationalize not doing so on "efficiency"
grounds.

There are some interesting quantum effects in human nature.  It doesn't
have to be a lot of trouble; "just a little too much" has the same effect.

I think maybe Forth programmers have a particularly low tolerance for
boilerplate, even when it is really and truly useful or even necessary.
I have seen this attitude while trying to convince Forth people of the
necessity of certain second-order interface functions in proposed extension
packages.

The interesting thing is, new Forth programmers tend to write long
procedures too.  Then, if somebody yells at them or twists their arm
for long enough, they will eventually start to use shorter and shorter
procedures, and then they are convinced.  When they go back to programming
in another language (e.g. C), the short procedure style will carry over,
and they will be more willing to pay the syntactic price.


Having said all this, I have to admit that, if you look at the C source
code for my C Forth 83 product, you will see one excruciatingly long
procedure in the midst of a collection of reasonably-sized ones.  I justify
this on (are you ready?) efficiency grounds.  It turns out that, in the
particular case of trying to implement Forth in C, the procedure call
overhead really does matter.  However, I claim that this is an unusual
case, because most Forth primitives do so *very* little work, and there
isn't much code over which to amortize the overhead.

Actually, it's not only the procedure call overhead that is the reason
for the big procedure in C Forth, it's also the inability to declare
global "register" variables, meaning that you would otherwise have to
keep the data stack pointer, interpreter pointer, and return stack
pointer in memory.  (Lest anybody think that I'm criticising C for
not allowing global "register" variables, I would like to point out
that there are excellent reasons for this restriction.  I leave the
reasoning as an exercise for the reader, with the following hint:
consider that C modules may be separately compiled and later linked
together).

Thus ends a long-winded answer to an excellent question.

Mitch Bradley

marc@noe.UUCP (Marc de Groot) (03/28/90)

In article <9003230003.AA15895@jade.berkeley.edu> wmb@MITCH.ENG.SUN.COM writes:
> [Lots of excellent stuff about the labor cost of factoring C code
>  into small routines deleted.]
>Having said all this, I have to admit that, if you look at the C source
>code for my C Forth 83 product, you will see one excruciatingly long
>procedure in the midst of a collection of reasonably-sized ones.  I justify
>this on (are you ready?) efficiency grounds.  It turns out that, in the
>particular case of trying to implement Forth in C, the procedure call
>overhead really does matter.
[ Irrelevancy to current discussion deleted ]
>Actually, it's not only the procedure call overhead that is the reason
>for the big procedure in C Forth, it's also the inability to declare
>global "register" variables, meaning that you would otherwise have to
>keep the data stack pointer, interpreter pointer, and return stack
>pointer in memory.  (Lest anybody think that I'm criticising C for
>not allowing global "register" variables, I would like to point out
>that there are excellent reasons for this restriction.  I leave the
>reasoning as an exercise for the reader, with the following hint:
>consider that C modules may be separately compiled and later linked
>together).

Mitch,
Thank you for addressing my favorite pet peeves about C.  The language
is highly touted as a systems programming language, yet has the annoying
deficiencies listed above.  Like you, I have implemented Forth in C and
run into exactly these problems.

Many C programmers actually argue with me that code written as many small
subroutines is harder to understand and maintain.  I submit to the net that
the understanding is a matter of practice, and that the maintenance argument
is entirely untenable.  The smaller and more regular the code modules are,
the easier it will be to change and replace them.

As for the inability to declare global register variables, I see your point
about multiple object files with separate compilation and linking.  However,
I would like a way to tell the compiler that I will not generate conflicts
related to the above, and to get global register variables.

Implementing Forth in C has pointed out to me in the most glaring manner
the shortcomings of C.

Finally, I have only one reason for wanting to use C on multiple-person
projects: I can read C code written by slobs.
I *can't* read Forth code written by slobs.


-- 
Marc de Groot (KG6KF)         |"...few people know what to do with a computer.
Noe Systems, San Francisco    | They decide that running an operating system
UUCP: uunet!hoptoad!noe!marc  | is a substitute for doing productive work."
Internet: marc@kg6kf.AMPR.ORG | -Chuck Moore