Path: utzoo!attcan!uunet!husc6!think!ames!mailrus!uflorida!novavax!proxftl!bill From: bill@proxftl.UUCP (T. William Wells) Newsgroups: comp.misc Subject: Re: Basics of Program Design Summary: efficiency is the reward of the diligent Message-ID: <430@proxftl.UUCP> Date: 5 Jul 88 23:55:39 GMT References: <901@td2cad.intel.com> <3061@rpp386.UUCP> <395@proxftl.UUCP>Distribution: na Organization: Proximity Technology, Ft. Lauderdale Lines: 114 In article , webber@porthos.rutgers.edu (Bob Webber) writes: > > In the real world, most compilers do NOT deal with tail recursion, > ^^^^^^^^^^ -- what makes you think your world is real? 14 years of commercial programming, the last 5 almost exclusively C. Languages used at least a year to earn paychecks, chronologically: Fortran, Basic, various database languages, COBOL, RPG, C. Over a half a dozen assembly languages used for commercial programming. Recent projects: project leader for improvements to a spelling checker for 6 (at the time) languages, currently doing R&D and for a grammar checker. Real enough? > > it not often being very useful to do this for C programs. > > Well, there is a free, high quality C compiler for 68000-based systems > and various others that does do tail recurision (i.e., gcc) among other > things. [However, it wouldn't catch this particular case -- perhaps > that is why you aren't using it? ] That's fine for 68000's (like the Sun I am working on) but not for IBM-PC's (our secondary machines) nor for the 100+ compilers our code is regularly compiled with. Relying on compilers to do tail recursion optimization would be slitting our own throats. > Optimizations fall into two categories: > those that handle the poor fit between language and architecture and those > that save programmer time. While for a language like SCHEME, this > would be viewed as a language-related optimization, in a language like > C it falls in the second category. Sorry, but using purely tail recursive routines does not save time for this programmer. Anywhere you would use tail recursion, I'd automatically code a while loop. (See below for why.) This means that your optimization actually falls in a third category: optimizations designed to let the programmer choose his own personal coding style. This does not apply to tail recursion discovered by the optimizer, however. Also, another real-world point: recursive routines in general need to be used carefully, to avoid excess resource consumption. As an example, I recently translated a rather complicated parsing routine from SCHEME to C. In the translated version were several recursive routines. One of the recursive routines was left alone, the greatest depth of recursion was the length of a grammar rule and removing the recursion would have been a real pain. Another got fixed (even though it required extensive recoding) because it was not possible to bound the stack usage. Our customers WILL NOT let us chew resources with wild abandon. > > A perfectly good algorithm can be a BAD choice in the real > > world. Yes, the algorithm is "good", which is to say, it works > > and is of the best possible order, but it is poorer than the > > standard implementation since its constant factor is going to be > > larger. > > Yes, you have learned the first lesson. Let me introduce you to the > second lesson: Not all code is executed the same amount of time. Thus > it is more important for some things to be fast than it is for others. > Strlen does not make sense as a function to be optimizing the death out > of. After all, if it is used all that much -- why isn't it a macro? Look buddy, I know both lessons. From the contents of your posting, I'd say better than you. One of the things I do better than damn near anyone else is make C programs go faster and get smaller. So here is a third lesson for you: try to make everything you do as good as you can. The notion that a routine is only going to be used a small fraction of the time is no excuse for sloppiness. Even if the routines are only going to be used 1% of the time, those damn little routines can end up eating much of your program's run time. To make this concrete, let me describe the profile output from a typical program after I have finished with it: The first few routines take about 5-10% of the execution time apiece. The remaining routines take less than a few percent apiece of the execution time. (This happens because I follow your second lesson: I beat the execution time hogs down until they are no longer hogs.) If the coder of those small change routines was as sloppy as you advocate, that sloppiness would result in many or most of those trivial routines being slower. Since those routines, in aggregate, make up a significant fraction of my program (from the standpoint of execution time), that means that my program will go significantly slower. Here is another example. A program I had to work with (a C version of the pattern generator for Knuth's hyphenator) spent 40% of its time in sscanf. There was NO GOOD REASON for sscanf(buf, "%d %s", &n, &ptr) to take that long, other than sloppy programming. (I replaced the call with my own scanning routines, the time spent in them was then 5% of the execution time.) Perhaps someone else can give an example of a program where strlen() did the same thing? And a final point. You have encouraged sloppiness in writing strlen() by justifying it with "that routine is never going to be executed much anyway." Even granted that proposition you have forgotten an important point. If you do not make it a habit to write good code ALL THE TIME, that means two things: 1) Your code will be generally less efficient than it could have been. 2) When it comes time to write efficient code, you will not have the experience needed to do it. Learning is a cumulative process. This little bitty routine that will not be executed much is as good a place for thinking about efficiency as any other, and possibly better. Do it enough and efficient coding becomes second nature. Then you will naturally write efficient programs and as a matter of course will not do stupid things like writing strlen() recursively.