[comp.lang.c] Portable C vs Efficient C or "Cost of Portability"

joecar@pyuxe.UUCP (04/02/87)

I'm looking for some information, experiences, opinions,
references, etc., that will help me understand the following
question:
 
   If one writes portable C, does it have to be less efficient 
   than non-portable C ?
 
In other words, does writing portable code add to the run time
overhead?  Conversely, can I write non-portable code that is
more efficient than the portable counterpart?

My project is looking at the "cost of portability", and would
appreciate any information (or journal references, etc.) that
reference any studies on the efficiency of portable C code.
Thanks in advance.
 
Joe Carfagno
...!bellcore!pyuxe!joecar

ron@brl-sem.UUCP (04/02/87)

In article <213@pyuxe.UUCP>, joecar@pyuxe.UUCP writes:
> 
> I'm looking for some information, experiences, opinions,
> references, etc., that will help me understand the following
> question:
>  
>    If one writes portable C, does it have to be less efficient 
>    than non-portable C ?
>  
One may right perfectly efficient portable code....most nonportable
code is just sloppy rather than machine dependant.  Consider the following
common errors:

1.  Assuming that there is "0" at location "0".

2.  Assuming int/char * are the same size.  This manifests itself in
	two ways.  First, sometimes people overload the same memory
	location with both pointers and ints.  This is what unions
	are for.  Unions should not be any less efficient than not
	having them (except where the compiler does initiallization
	wrong).  The second is passing things to subroutines without
	casting.  The assignment operater is pretty forgiving...it
	knows what the types on both sides need be.  Subroutines aren't
	nearly so clairvoyant, one needs to settle on what the argument
	type is and force any calls to be made to that type.  On machines
	where it doesn't matter, the cast is a no-op, on others it is
	essential.

3.  Saying int when you mean long...
	Of course there is no way to determine this really portably,
	but you can make a small machine header file that sets up
	typedefs appropriately.

The above, when done correctly should have no effect on efficiency
(except that it will cause the code to work on machines where it
wouldn't before).

Now there are a few additional things that require some thinking to
do efficiently:

4.  Assuming byte ordering in larger objects.
	There are some real quick get arounds if you "know" your machine's
	byte order, but this may be wrong on other machines.  But this still
	can be accomodated.  For example the 4.2 network code has a macro
	that converts to network byte order which again is essential for
	machines with the other order, but is a no-op on the ones where
	the byte order is the same.

5.  Making assumptions about the location of items and padding within
	structures.  This one requires some thought.

-Ron

jbuck@epimass.UUCP (04/02/87)

In article <213@pyuxe.UUCP> joecar@pyuxe.UUCP asks:
>   If one writes portable C, does it have to be less efficient 
>   than non-portable C ?

98.44% of the time, there is absolutely no penalty for writing portable
code.  For example, casting pointers to the right type, and using
"sizeof" rather than putting numbers in the code, and making sure
to use "long" instead of int when a value may exceed the range of
a 16 bit integer, generates essentially the same code as doing it
sloppy does.  And it's less likely to be buggy.  This is because
casts that don't do anything on a particular architecture generate
no code, and expressions involving sizeof are evaluated at compile
time.  SysV vs BSD differences can be taken care of with #ifdefs
where they arise.  Again, no time penalty.

Having said all this, I will make a confession.  I wrote a rather
large and complex simulator/symbolic debugger for a DSP board
designed here at Entropic.  It runs on at least 5 different versions
of Unix (and one alleged Unix:  Eunice) including one with 16 bit
ints and 32 bit pointers (thank you, lint!).  The only problem
encountered in porting was that I had assumed sprintf returns a
pointer to the data (as in bsd) while it returns the string length on
Sys V.  But it makes one glaring nonportable assumption:  that the
machine it's running on is two's complement.  This is because it's
simulating a machine that does two's complement math; the alternative
would be to make the code run much slower.  So yes, there are cases
where truly portable code would give an efficiency penalty.  But they
are rare.  

Actually, it turns out that casting from unsigned to signed ints and back
is guaranteed to simulate two's complement behavior even on non-two's
complement machines (see K&R).  Had I known this, I could have done the
simulator portably without a time penalty.  But I don't think I'll
go back and change it now.
-- 
- Joe Buck    {hplabs,ihnp4,sun,ames}!oliveb!epimass!jbuck
	      seismo!epiwrl!epimass!jbuck  {pesnta,tymix,apple}!epimass!jbuck

jtr485@umich.UUCP (04/04/87)

In article <710@brl-sem.ARPA>, ron@brl-sem.UUCP writes:
> One may right perfectly efficient portable code....most nonportable
> code is just sloppy rather than machine dependant.  Consider the following
> common errors:
> 
> 1.  Assuming that there is "0" at location "0".
> 2.  Assuming sizeof(int) == sizeof(char *)
>	passing things to subroutines without
> 	casting.  The assignment operater is pretty forgiving...it
> 	knows what the types on both sides need be.
Assignment had better be forgiving since type casts are defined in terms of it.

> 3.  Saying int when you mean long...
> 	Of course there is no way to determine this really portably,
> 	but you can make a small machine header file that sets up
> 	typedefs appropriately.
short, int, long is the dumbest part of C.  The language should have said
there will be a class of types int<ext> where <ext> is the number of bits.
But not have specified how many of these there are or what <ext> values
they should have.  just poor planning. take your header file ONE step further
add macros like 
#define int %$@very bad boy using INT#$
#define long %$@very bad boy using LONG#$
so you CANNOT use int, long etc.

> 4.  Making assumptions about byte order.
> 5.  Making assumptions about the location of items and padding within	structs.
how about:
  6.  Passing of variable parameter lists.
   	What?  You mean it can't be done portably?  Oh well, never mind. 

> -Ron
--j.a.tainter

hrubin@osupyr.UUCP (04/04/87)

I frequently find that reasonably good code can be portable, but rarely do I
find efficient code portable.  In fact, rarely do I find C code (or any other
HLL) to be efficient.  Assuming that the compiler allows you to put all the
variables (but not, of course, the arrays)in registers, the following fragment
of code is better on VAXen

	t = *(m++) ^ *(h++);
	*(k++) = t;
	*(z++) ^= t;

than

	t = m[x] ^ h[x];
	k[x] = t;
	z[x] ^= t;
	x++;

while on the pyramid the reverse is true.  This is because of the computer
architecture.  When operations not supported by C are needed, clearly the
problem is worse. 
-- 
Herman Rubin
Until mid May:
Department of Statistics, Cockins Hall, 1958 Neil Avenue
Ohio State University, Columbus OH43210
hrubin@osupyr or cbosgd!osu-eddie!osupyr!hrubin or ts0474@ohstvma.bitnet
"Permanent":
Department of Statistics, Purdue University, West Lafayette IN47907
hrubin@l.cc.purdue.edu or ihnp4!pur-ee!stat-l!hrubin or hrubin@purccvm.bitnet

chris@mimsy.UUCP (04/07/87)

[This is long, but if you skip all the rest, please read the last
paragraph.]

In article <170@osupyr.UUCP> hrubin@osupyr.UUCP (Herman Rubin) writes:
>I frequently find that reasonably good code can be portable, but rarely do I
>find efficient code portable.

By which, I guess, you mean that it takes different `tricks' on
different machines to make code efficient, not that because the code
has been tweaked for efficiency on one machine, it no longer runs at
all on others.

>In fact, rarely do I find C code (or any other HLL) to be efficient.

I think you will have to define `efficient' first.

>Assuming that the compiler allows you to put all the variables (but
>not, of course, the arrays) in registers, the following fragment of
>code is better on VAXen
[reproduced below in expanded form]
>while on the pyramid the reverse is true.

Really?  Let us see.

First, here is the code being tested.  You did not specify whether the
arrays were to be on the stack or whether they were to be static (or,
equivalently, global), so I tried it both ways.

	% cat xx.c
	/* variants:
		STACK => stack arrays (else STATIC => static): meaningful
			iff SUB
		SUB => subscript code (else ptr)
	*/

	f()
	{
		register int t;
	#ifdef SUB
		register int x;
	#ifdef STACK
		int m[4], h[4], k[4], z[4];
	#else
	#ifndef STATIC
		oops!
	#else
		static int m[4], h[4], k[4], z[4];
	#endif /* STATIC */
	#endif /* STACK */
	#else /* not SUB, so PTR */
		register int *m, *h, *k, *z;
	#endif /* SUB */

	#ifdef SUB
		t = m[x] ^ h[x];
		k[x] = t;
		z[x] ^= t;
		x++;
	#else
		t = *m++ ^ *h++;
		*k++ = t;
		*z++ ^= t;
	#endif
	}

And a Makefile to convert this to three assembly variants:

	C2ASM=	cc -O -S

	all: v0.s v1.s v2.s

	v0.s: xx.c
		${C2ASM} -DSTACK -DSUB xx.c; mv xx.s $@

	v1.s: xx.c
		${C2ASM} -DSTATIC -DSUB xx.c; mv xx.s $@

	v2.s: xx.c
		${C2ASM} -DPTR xx.c; mv xx.s $@

Now compare all three.  In reverse order:

	/* Vax: PTR */
	xorl3	(r9)+,(r10)+,r0
	movl	r0,r11
	movl	r11,(r8)+
	xorl2	r11,(r7)+

Not bad.

	/* Pyr: PTR */
	movw	(lr1),lr0
	xorw	(lr2),lr0
	addw	$4,lr2
	addw	$4,lr1
	movw	lr0,(lr3)
	addw	$4,lr3
	movw	(lr4),tr0
	xorw	lr0,tr0
	movw	tr0,(lr4)
	addw	$4,lr4

Well, not so great.  All those ++es expand to one instruction each.

Next:
	/* Vax: STATIC SUB */
	xorl3	L16[r10],L17[r10],r0
	movl	r0,r11
	movl	r11,L18[r10]
	xorl2	r11,L19[r10]
	incl	r10

Not so bad either.  In fact, this takes only one more instruction,
and longer addressing modes.

	/* Pyr: STATIC SUB */
	movw	L13[lr1 * 4] ,lr0
	xorw	L14[lr1 * 4] ,lr0
	movw	lr0,L15[lr1 * 4] 
	movw	L16[lr1 * 4] ,tr0
	xorw	lr0,tr0
	movw	tr0,L16[lr1 * 4] 
	addw	$1,lr1

This is clearly better than Pyr: PTR.  (The `* 4' is an addressing
mode; these are indeed single instructions.)

Finally:
	/* Vax: STACK SUB */
	xorl3	-16(fp)[r10],-32(fp)[r10],r0
	movl	r0,r11
	movl	r11,-48(fp)[r10]
	xorl2	r11,-64(fp)[r10]
	incl	r10

Still not so bad.  A bit slower than STATIC SUB, since -offset(fp)'s
must be computed each time, but offsets of -64..63 all fit in the
instruction itself on a Vax.

	/* Pyr: STACK SUB */
	mova	-16(cfp),lr0
	movw	(lr0)[lr1 * 4] ,lr0
	mova	-32(cfp),tr0
	xorw	(tr0)[lr1 * 4] ,lr0
	mova	-48(cfp),tr0
	movw	lr0,(tr0)[lr1 * 4] 
	mova	-64(cfp),tr0
	movw	(tr0)[lr1 * 4] ,tr1
	xorw	lr0,tr1
	movw	tr1,(tr0)[lr1 * 4] 
	addw	$1,lr1

I do not know whether the offsets fit, but this is basically like
the PTR version, although I count 11 instructions, and PTR was 10.

What have we really learned?  Very little.  Unless this is an inner
loop of an inner loop, none of these variations will make much
difference.  And if it *is* an inner loop of an inner loop, maybe
we should use yet another variant:

	register int *h = h_arr, *m = m_arr, *k = k_arr, *z = z_arr;
	register int t, x;
	...
	t = m[x] ^ h[x];
	k[x] = t;
	z[x] ^= t;
	x++;

which, on the Vax, becomes

	xorl3	(r9)[r6],(r11)[r6],r0
	movl	r0,r7
	movl	r7,(r10)[r6]
	xorl2	r7,(r8)[r6]
	incl	r6

which should be ever so slightly faster than the static version.
The `extra' incl in this loop over the pointer version may actually
cost less than you think:  With a loop around the outside, counting
to (say) 1000, we get

	clrl	r6
L2000001:
	xorl3	(r9)[r6],(r11)[r6],r7
	movl	r7,(r10)[r6]
	xorl2	r7,(r8)[r6]
	aoblss	$1000,r6,L2000001

The add, test, and branch instruction makes this *better* than the
pointer variant, which requires two separate instructions to do
the loop!  (It is conceivable that the address computation in this
loop, being more complex than in the pointer loop, makes this
version slower than the other, but I doubt it---and if you really
care, go measure it yourself! :-) )

On the Pyramid, it compiles to this:

	movw	(lr2)[lr5 * 4] ,lr4
	xorw	(lr0)[lr5 * 4] ,lr4
	movw	lr4,(lr1)[lr5 * 4] 
	movw	(lr3)[lr5 * 4] ,tr0
	xorw	lr4,tr0
	movw	tr0,(lr3)[lr5 * 4] 
	addw	$1,lr5

which is most likely slightly faster than the static version, and
is clearly faster than the other versions.

But (and this is an important `but') unless you *know* your program
spends a great deal of its time in this loop, there is little point
in attempting to optimise it in source code.  That such optimisations
may be machine dependent is immaterial when said optimisations are
unnecessary, and where they *do* make a difference, if you expect
the code to be ported, *point them out*!
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

doug@edge.UUCP (04/11/87)

>    If one writes portable C, does it have to be less efficient 
>    than non-portable C ?
> 
> My project is looking at the "cost of portability"...

I can think of few cases where you can write non-portable but more-efficient
C code.  Generally you will find that if you write efficient code, the code
will be portable /but the efficiency will not/.  In other words, you can
tune your code to the particular compiler and machine you're running on, and
although that code /will/ run on another machine, it won't be fast.

The standard areas here are:  Register variables, of course.  Subscripting
versus pointers ( a[i++]  versus *pa++ ).  Pre/post increment/decrement,
especially in subscripts ( a[i]=0; a[i+1]=0; i+=2; versus a[i++]=0; a[i++]=0;).
Size of integers ( on a 68000 system, integers are usually 32-bit, but if
you multiply them you'll want 16-bit ints ).  On some machines, "char"s are
slow to access and "short"s would be better; vice versa on other machines.
"Unrolling" loops may be advantageous on one machine, but will defeat the
instruction cache on another.

Now for a more general thought.  Hope this doesn't start a flame war.

If you are more concerned about efficiency than about portability, then
consider this:  There are 4 basic reasons for writing a particular program
in C instead of in assembler:
 a)  If efficiency is of little consequence -- the program is only going to
     be run once or twice;
 b)  If portability is important;
 c)  If the programmer is ignorant of assembler; or
 d)  If the boss wants "BIC lighter" programmers: cheap, disposable, and
     easily replaceable.

Thus (from the point of view of an outsider) if you're willing to scrap
portability in return for efficiency, you should be programming in assembler
instead of C.  From the point of view of the programmer, reason (c) might
be an overriding consideration.  From the point of view of the management,
reason (d) might be an overriding consideration.

-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona

bzs@bu-cs.UUCP (04/12/87)

I believe "portability" will become the "goto-less" programming of the 80's.

In both cases the suggestions should be applied 98% of the time.

The use of goto programming before "the movement" was rampant and
causing all sorts of difficulty. I experienced that, I worked with
large Fortran programs in which GOTO was the favorite control
construct, necessary or not and usually badly thought out (like
jumping out and back into the same code segment so the new code can be
put at the bottom, no joke.) It was argued that some of this Fortran
code was getting all the useful work done in the interstices of the
gotos :-)

Then came the prosletytes, for example the professors who gave an F
on any programming assignment that contained a goto, no matter what
the reason (I've personally known professors here with that policy
only a few years ago.)

Of course that was nonesense.

We finally (at least those of us getting work done) came to some
agreement that goto's were ok under certain conditions (eg. jumping out of
a multiply nested loop on an error condition to quickly "give up",
rather than adding noisy bells and whistles flags.)

I assume we are somewhere in the same cycle with portability, with the
same healthy result.

As with gotos, we want to think *real* hard before introducing
non-portability into a program. For example, if you can get
significant performance increase (which you can demonstrate you can
benefit from) and even conditionalize the code so it decides whether
to compile portably or not, then go to it. And know the consequences.

That's all. Common sense. 98% of the non-portable code smashed (hopefully)
just like 98% of the gotos. We all win.

	-Barry Shein, Boston University

terryk@encore.UUCP (Terence Kelleher) (04/13/87)

Why use C for efficient code? I write controller applications for I/O 
systems and would rarely consider assembly, even though efficiency is
a prime concern.  Most C compilers will generate code ,from good source
that is as good as the average assembly code.  The C code has a better
chance of being maintainable, by the author or others. C code can be
generated in a fraction of the time that it would take to write the
same functions in assembly.

I don't think these points are at all original.  I have worked on disk
systems, communications processors and graphics. In these areas, C has
seemed to be the language of choice, even though the nature of low level
I/O precludes portability.

I summery, C is faster, cheaper and far less hassle than assembly.  Even if
you don't care about portability, C makes sense.

Terry Kelleher
Encore Computer Corp.

john@frog.UUCP (04/14/87)

> If you are more concerned about efficiency than about portability, then
> consider this:  There are 4 basic reasons for writing a particular program
> in C instead of in assembler:
>  a)  If efficiency is of little consequence -- the program is only going to
>      be run once or twice;
>  b)  If portability is important;
>  c)  If the programmer is ignorant of assembler; or
>  d)  If the boss wants "BIC lighter" programmers: cheap, disposable, and
>      easily replaceable.

You forgot

   e)  Reducing the opportunity for bugs by roughly a factor of 10.

Most estimates of the average number of bugs in a program indicate that the
number is roughly a constant fraction of the number of lines (which I believe
is typically 10%), independant of language.  A program written in C will
typically be 1/10 the size of an equivalent assembly program (depending on
the complexity of the C statements), so there are (roughly) 1/10 as many
thoughts to mis-think -- in assembly, they may go wrong on a less grand
scale, but they still are bugs.  I believe there was an article in the
most recent Comm. ACM about this general topic.

My favorite quote, which didn't arise from this consideration, but does
have the same philosophical bent:  "Presumably it was essential to get the
wrong answer as fast as possible."  Kernighan & Plauger, The Elements of
Programming Style.


--
John Woods, Charles River Data Systems, Framingham MA, (617) 626-1101
...!decvax!frog!john, ...!mit-eddie!jfw, jfw%mit-ccc@MIT-XX.ARPA

"Happiness is the planet Earth
	in your rear-view mirror."
		- Sam Hurt, in "Eyebeam, Therefore I Am"

henry@utzoo.UUCP (Henry Spencer) (04/16/87)

>   If one writes portable C, does it have to be less efficient 
>   than non-portable C ?

It depends on what kind of "non-portable C" you are thinking about.

C does let you get moderately intimate with the machine if you really want
to.  On the pdp11, with a compiler that didn't do global optimization, it
was possible for someone familiar with the machine to write C in such a
way as to control the exact sequence of instructions.  (This is the whole
reason for the ++ and -- operators, for example, since they allow fairly
direct control of the pdp11 autoincrement/decrement addressing modes.)
Code that has been done this way for the sake of absolute maximum speed
is inherently fairly machine-specific.  ON THE OTHER HAND, if the code
avoids making stupid assumptions, it WILL work on other machines, although
a lot of the efficiency tailoring may be wasted.  Such code is usually hard
to read, but its meaning is often machine-independent even though the
idioms used to express it are not.

In the absence of very good compilers, C written without such machine-
specific tweaking will be less efficient than C written with it.

HOWEVER.  If you are not concerned with squeezing every last microsecond
out of the code in arcane ways, and are willing to content yourself with
the standard of efficiency set by typical C compilers (not superb, but
good compared to a lot of other high-level languages and their compilers),
the situation changes.  Then there is generally little or no penalty for
writing portable code, assuming you know what you're doing.

Since only a minute fraction of all C code is sufficiently crucial to
justify machine-specific code greasing, the best general statement is that
well-written portable C is not significantly less efficient than unportable
C.  This is true even without considering how "efficient" the code is when
it won't run on your new faster machine.
-- 
"We must choose: the stars or	Henry Spencer @ U of Toronto Zoology
the dust.  Which shall it be?"	{allegra,ihnp4,decvax,pyramid}!utzoo!henry

doug@edge.UUCP (04/17/87)

I was afraid of this.  I figured I'd get some flak when I wrote:

me > If you are more concerned about efficiency than about portability, then
me > consider this:  There are 4 basic reasons for writing a particular program
me > in C instead of in assembler:
me >  a)  If efficiency is of little consequence -- the program is only going to
me >      be run once or twice;
me >  b)  If portability is important;
me >  c)  If the programmer is ignorant of assembler; or
me >  d)  If the boss wants "BIC lighter" programmers: cheap, disposable, and
me >      easily replaceable.

Comment 1:

> You left off reason (e), I hope, and that is "If you want Readability and
> therefore Maintainability".

A C program is no more readable to a C programmer than an assembler program
is to an assembler programmer.  Ditto for maintainability.  The issues of
Readability and Maintainability come back to points (c) and (d) above; in
other words, whether we're talking about C programmers or assembler
programmers.

The notorious lack of "commenting" by supposedly professional C programmers
really compounds this problem.  Professional assembler programmers make dang
sure their code is well commented, and the comments are maintained as the
program is maintained.  This is not a black mark against the C language itself,
but rather against the smug arrogance of the scads of C programmers who
believe that C is so inherently readable and maintainable that comments are
superfluous.

Comment 2:

> You forgot
> 
>    e)  Reducing the opportunity for bugs by roughly a factor of 10.

I'd like to see some studies on this.  I'd believe it for languages like
FORTRAN, COBOL, and Ada which prevent the programmer from making really
stupid mistakes like not being able to tell a string literal from an
array of characters, or arithmetic overflow/divide-by-zero/floating point
overflow/underflow, or the infamous "if (x=y)" type of stuff that C
allows.  C is basically a PDP-11 assembler in a fancy suit, and brings
with it all of the screw-up possibilities that you get in real assembler.

C is often used in applications where it is a poor fit, and this causes
lots of room for screw-ups as the programmer tries to work around the
limitations of the language.  Device drivers in particular.  (Isn't
"portable device driver" almost an oxymoron?).  All languages, including
assembler, are often used in applications where they are a poor fit, with
the result that the code tends to be excessively buggy.  C zealots usually
deny that there exist any applications for which C is a poor fit.

Furthermore, C is more difficult to debug than assembler, because (like
all other high-level languages) it is distanced from the machine.  To
debug C you either need to know assembler, and to debug it at the assembler
level, or you need a symbolic debugger.  A lot of systems don't *have*
symbolic debuggers, or the ones they have are not full-functioned.  And
I haven't seen one yet that will allow a C programmer to make a patch to
the code *in C*.

And finally, <<<Break out the fireproof suits!>>>  all of the above was
based on "programmers of equal competency".  But consider that point (d)
above virtually guarantees that the ever-increasing pool of marginally
competent programmers (you know the ones, "I got me a PeeCee and learnt
meself how to program in Basic and C") will be hired by penny-pinching
companies to program in C.

I'm not saying that you personally (whoever you may be) are marginally
competent.  I *am* saying that if you restrict yourself to the C programming
job market, you will be competing with a lot of bozos who are willing to work
for peanuts (hey, it beats flipping burgers for a living!).  And in return for
this limited pay, you will get to maintain the programs those bozos wrote.  If
you don't like it, the boss can dump you and hire another bozo; he's got 2
dozen resumes on file.  *That's* what I mean about BIC lighter programmers.

-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona

al@gtx.com (0732) (04/17/87)

doug@edge.UUCP (Doug Pardee) writes:

> consider this:  There are 4 basic reasons for writing a particular program
> in C instead of in assembler:
>  a)  If efficiency is of little consequence -- the program is only going tome >      be run once or twice;
>  b)  If portability is important;
>  c)  If the programmer is ignorant of assembler; or
>  d)  If the boss wants "BIC lighter" programmers: cheap, disposable, and
>      easily replaceable.

One important reason not mentioned above is development time.  
Unless there are unusual efficiency considerations, The C programmer can
program rings around the assembly language programmer because there
are fewer low-level discriminations to be made.  A well-known folk theorem
of computer science says that it takes about as long to write and debug one 
line of, say, Fortran or C as one line of assembly language. Since the 
assembly language program is considerably longer, it takes longer to write.

> A C program is no more readable to a C programmer than an assembler program
> is to an assembler programmer.  Ditto for maintainability.  The issues of
> Readability and Maintainability come back to points (c) and (d) above; in
> other words, whether we're talking about C programmers or assembler
> programmers.

"Higher-level" languages such as C and Fortran were all designed and 
implemented by exceptionally competent ASSEMBLY LANGUAGE PROGRAMMERS 
disgusted by the unreadability and unmaintainability of assembly language. 
Portability was always a secondary issue.

> And finally, <<<Break out the fireproof suits!>>>  all of the above was
> based on "programmers of equal competency".  But consider that point (d)
> above virtually guarantees that the ever-increasing pool of marginally
> competent programmers (you know the ones, "I got me a PeeCee and learnt
> meself how to program in Basic and C") will be hired by penny-pinching
> companies to program in C.

At least the kid with the PC is educable.  Spare me from the 
hardware retread who *knows* that all there is to programming is learning 
the op-codes for the processor.


       --------------------------------------------------------------
      | Alan Filipski,  GTX Corporation, Phoenix, Arizona 85283, USA |
      | ihnp4!arizona!sunburn!gtx!al
       --------------------------------------------------------------

God took seven days to create the world; 
an APL programmer could do it in one line.

henry@utzoo.UUCP (Henry Spencer) (04/19/87)

> ...(This is the whole
> reason for the ++ and -- operators, for example, since they allow fairly
> direct control of the pdp11 autoincrement/decrement addressing modes.)

Oops.  This seemed so obviously true, given the close match between the
language and the hardware, and the compiler's willingness to do the right
thing, that I never checked for sure.  Turns out I was wrong:  ++ and --
existed in B, well before the 11 entered the picture.  They did reflect
some influence from the pdp7 hardware, according to Dennis, but the B
implementation didn't do anything terribly clever with them.

Modify "This is the whole reason" to "This was the most important use";
I think I can defend that one...
-- 
"We must choose: the stars or     Henry Spencer @ U of Toronto Zoology
the dust.  Which shall it be?"    {allegra,ihnp4,decvax,pyramid}!utzoo!henry

amos@instable.UUCP (Amos Shapir) (04/19/87)

Most of  the arguments on  this group stem  from the fact  that C
became too good for its own good. C started as not much more than
a  structured-programming  assembly  macros (something  like  the
800-line 'foogol'  compiler posted to net.sources  not long ago).
It was meant to be a quick & dirty front end to assembly. Then it
was discovered  that most  of the dirty  tricks had  parallels on
other architectures, and C became portable. K&R did a magnificent
job of  describing what is supposed  to be portable, and  what is
left quick & dirty.

However, since C was not meant  to be initially portable, many of
the checks and guards of high level languages were left out. That
made  it a  nice language  to program  in. Which  made it  widely
distributed. Which caused much portable  code to be written in it
- exactly  the type of  programming HLL's checks and  guards were
invented for...
-- 
	Amos Shapir
National Semiconductor (Israel)
6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel  Tel. (972)52-522261
amos%nsta@nsc.com {hplabs,pyramid,sun,decwrl} 34.48'E 32.10'N

manis@ubc-cs.UUCP (04/21/87)

In article <658@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:

>> You left off reason (e), I hope, and that is "If you want Readability and
>> therefore Maintainability".
>
>A C program is no more readable to a C programmer than an assembler program
>is to an assembler programmer.  Ditto for maintainability.  The issues of
>Readability and Maintainability come back to points (c) and (d) above; in
>other words, whether we're talking about C programmers or assembler
>programmers.
I must respectfully disagree with these remarks. Admittedly much of the C I
have seen (say in mod.sources) is disgracefully written. However, I write C
as if it were Pascal. I am also a quite competent assembler programmer (for
several *dozen* different machines). I can read my C and Pascal code; I
cannot read my assembly code anywhere as well, even when it is carefully
written.

>The notorious lack of "commenting" by supposedly professional C programmers
>really compounds this problem.  Professional assembler programmers make dang
>sure their code is well commented, and the comments are maintained as the
>program is maintained.
Comments are not very useful in programming. I once maintained a BCPL
compiler which had almost no comments in it. The front end (parser) was a
model of clarity; the back end (code generator) was tangled, contorted and
used an immense number of coding tricks (it generated very good code,
though). The difference was the programmers; no number of comment lines
would have made that code generator clear. 

>> You forgot
>> 
>>    e)  Reducing the opportunity for bugs by roughly a factor of 10.
>
>I'd like to see some studies on this. 
Take a look through the back issues of IEEE Transactions on Software
Engineering. All other things being equal, the number of bugs is generally
directly proportional to the number of source lines. 

>...  C is basically a PDP-11 assembler in a fancy suit, and brings
>with it all of the screw-up possibilities that you get in real assembler.
Where does this strange idea come from? The newest trends in C compilers are
to very fussy type-checking (which is where such things as function
prototypes originate). Admittedly, C compilers can't detect subscript range
violations, but just about all type errors can be detected by some of the
better compilers, or by lint.

>...  C zealots usually
>deny that there exist any applications for which C is a poor fit.
I'm certainly not a C zealot, but I can't think of *any* application for
which C is a poor choice as a language but assembler is a good one. Of
course, that does not mean that a particular C compiler will give code of
adequate efficiency.

>Furthermore, C is more difficult to debug than assembler, because (like
>all other high-level languages) it is distanced from the machine. 
Well, dbx certainly reduces that distance. On the other hand, I know that I
write C programs with a lot fewer errors than my assembler programs, and
therefore my C programs don't make as many trips to the debugger.
Mind you, I use lint a lot on UNIX systems, and the C compiler I use on my
micro (Mark Williams C for the Atari ST) does a very good job of
type-checking.

Doug goes on to remark that C programmers just switched from Basic. Indeed,
there are many incompetent C programmers, just as there are many incompetent
assembler programmers (many of the latter are engineering graduates who had
just *one* programming course). Has nothing much to do with anything, as far
as I can see.

Doug, if you want to program in assembler, then by all means do so. But
please don't attack C because it isn't assembler. I agree that the quality
of much C code is poor; but if you care about this matter, are you out there
running structured programming courses for these people? 

-----
Vincent Manis                {seismo,uw-beaver}!ubc-vision!ubc-cs!manis
Dept. of Computer Science    manis@cs.ubc.cdn
Univ. of British Columbia    manis%ubc.csnet@csnet-relay.arpa  
Vancouver, B.C. V6T 1W5      manis@ubc.csnet
(604) 228-6770 or 228-3061

"Long live the ideals of Marxism-Lennonism! May the thoughts of Groucho
 and John guide us in word, thought, and deed!"

hrubin@osupyr.UUCP (Herman Rubin) (04/22/87)

I have found it necessary to include the entire article for the convenience of
the reader; I have placed it at the end.

I have frequently encountered situations where the operations I wish to use are
extremely difficult in the totally inadequate HLL's available.  In other 
situations, the problem is a (frequently vast) choice of algorithms; the
programmer who thinks in BASIC or FORTRAN or C cannot begin to appreciate 
what can be done.  I am inclined to doubt that most of them retain the ability
to understand what a computer can do if its instruction capability is utilized.

I find machine language easy to understand, even easier than the weird
constructs of the HLL's.  The difficulty of writing good programs is the
now unnecessary awkwardness of assembler constructs.  If an intelligently
designed assembler, or even macro translator, were available, this could
be done.  Alternatively, if the intelligent and resourceful
programmer could define his types, define operations (e.g., define "`\'" to
be the "bit clear" operation on most machines, and to inform the compiler
that this is a hardware operation with a syntax similar to the other logical
operations, or define ">>>" and "<<<" to be double register shifts),
use the condition codes instead of the restricted "?" in C
(how do you use integer overflow? I want to use it), allow the programmer
to force things to be in registers or even short register vectors, etc., we
may reach the stage that a HLL may be used for intelligent programming.

Learning the op-codes (and their execution times and overlap properties)
is not sufficient, but it is necessary.  Does learning HLL thinking prevent,
and not just inhibit, learning to make a computer do what the user, and not
the compiler designer, wants to do?  We do not know how to design a good
compiler; a flexible "SuperC" may do a fair job.

In article <215@gtx.com>, al@gtx.com (0732) writes:
> 
> doug@edge.UUCP (Doug Pardee) writes:
> 
> > consider this:  There are 4 basic reasons for writing a particular program
> > in C instead of in assembler:
> >  a)  If efficiency is of little consequence -- the program is only going tome >      be run once or twice;
> >  b)  If portability is important;
> >  c)  If the programmer is ignorant of assembler; or
> >  d)  If the boss wants "BIC lighter" programmers: cheap, disposable, and
> >      easily replaceable.
> 
> One important reason not mentioned above is development time.  
> Unless there are unusual efficiency considerations, The C programmer can
> program rings around the assembly language programmer because there
> are fewer low-level discriminations to be made.  A well-known folk theorem
> of computer science says that it takes about as long to write and debug one 
> line of, say, Fortran or C as one line of assembly language. Since the 
> assembly language program is considerably longer, it takes longer to write.
> 
> > A C program is no more readable to a C programmer than an assembler program
> > is to an assembler programmer.  Ditto for maintainability.  The issues of
> > Readability and Maintainability come back to points (c) and (d) above; in
> > other words, whether we're talking about C programmers or assembler
> > programmers.
> 
> "Higher-level" languages such as C and Fortran were all designed and 
> implemented by exceptionally competent ASSEMBLY LANGUAGE PROGRAMMERS 
> disgusted by the unreadability and unmaintainability of assembly language. 
> Portability was always a secondary issue.
> 
> > And finally, <<<Break out the fireproof suits!>>>  all of the above was
> > based on "programmers of equal competency".  But consider that point (d)
> > above virtually guarantees that the ever-increasing pool of marginally
> > competent programmers (you know the ones, "I got me a PeeCee and learnt
> > meself how to program in Basic and C") will be hired by penny-pinching
> > companies to program in C.
> 
> At least the kid with the PC is educable.  Spare me from the 
> hardware retread who *knows* that all there is to programming is learning 
> the op-codes for the processor.
> 
> 
>        --------------------------------------------------------------
>       | Alan Filipski,  GTX Corporation, Phoenix, Arizona 85283, USA |
>       | ihnp4!arizona!sunburn!gtx!al
>        --------------------------------------------------------------
> 
> God took seven days to create the world; 
> an APL programmer could do it in one line.

-- 
Herman Rubin
Until mid May:
Department of Statistics, Cockins Hall, 1958 Neil Avenue
Ohio State University, Columbus OH43210
hrubin@osupyr or cbosgd!osu-eddie!osupyr!hrubin or ts0474@ohstvma.bitnet
"Permanent":
Department of Statistics, Purdue University, West Lafayette IN47907
hrubin@l.cc.purdue.edu or ihnp4!pur-ee!stat-l!hrubin or hrubin@purccvm.bitnet

doug@edge.UUCP (Doug Pardee) (04/25/87)

I see no reason to repeat my previous postings on the use of assembler for
some non-portable applications.  But I would like to expand on a couple of
points:

> I'm certainly not a C zealot, but I can't think of *any* application for
> which C is a poor choice as a language but assembler is a good one.

Well, let's see.  There are some obvious ones, like real-time life-support
or safety-monitor applications in which it is absolutely necessary to show
that in the worst case, the response time will be less than x microseconds.
(This requires not only assembler language, but also relatively primitive
CPUs and system designs in which the hardware performance can be determined
exactly, none of this "depends on the percent of cache hits" stuff allowed.)

Less obviously, almost anything having to do with character handling.  Most
CPUs provide hardware instructions for string copies, compares, and searches.
C provides no statements which could be compiled into these instructions.

Also, anything that has to do with direct hardware manipulation (like device
drivers).  On the 68000, for instance, the assembler programmer has the
MOVEP instruction which allows easy writing of multiple bytes to I/O
devices.  In C, there's no statement that can do this, so you end up with
  *(char *)0x12345 = (val >> 8) & 0xFF;
  *(char *)0x12347 = val & 0xFF;

Another reason for writing device drivers in assembler is to have absolute
control over when and how the I/O locations are referenced.  Trying to
program an ordinary 2681/68681 DUART, for instance, you have to have a certain
amount of time pass between references.  And certain locations are off-limits;
you don't want the C compiler generating a >1 byte fetch which accidently
includes a no-no byte (this is with memory-mapped I/O, of course).

> Of course, that does not mean that a particular C compiler will give code of
> adequate efficiency.

This is the more important consideration.  There's a lot of talk about what
compilers *could* do in optimization.  And somehow that's gotten translated
into the mistaken belief that all of this wonderful optimization is really
happening, or will happen soon.

The truth is that most C compilers are based on PCC, and produce absolutely
terrible code.  Any programmer who has only PCC available should *not* believe
any of the jive about C code being efficient.

And even with the decent optimizing compilers, code size in C is awful.  See
the discussion in comp.sys.amiga about how to get "hello, world" down from
8K to 1K!  In assembler, it'd be maybe 50 bytes.

> Doug goes on to remark that C programmers just switched from Basic.

I hope that isn't how it sounded.  What I was trying to say is that C
programming is a rather attractive profession to someone who isn't really a
professional-level programmer, and it is just too easy for them to get into.
The existence of these people drags down the average quality of C code, and
puts heavy strain on salaries and working conditions for *all* C programmers.

Don't get lost, I'm getting to a subtle point (I hope).  Because there are
so many C programmers, who will work for low pay under less-than-thrilling
conditions, many employers insist that their employees write in C.
Subpoint #1: This really is the employer's right. Subpoint #2: This guarantees
that they can get cheap replacements with little effort.  Subpoint #3: The
employers don't really have to insist too hard, because the "programmers" they
hire don't know anything but C anyway.  Subpoint #4: This is a great
situation for those not-too-competent programmers; even though the pay is low
and there's a lot of competition for the jobs, things are a lot worse in the
burger-flipping business.

Major Point: what is good for the employer and for the not-so-capable
programmers is not necessarily in the best interests of genuinely capable
programmers.

Corollaries: it is also not necessarily in the product's best interests.  It
is also not necessarily in the customers' best interests (for those cases when
the code is part of a product).

> please don't attack C because it isn't assembler.

Perhaps that's how it sounded, but 'tain't so.  I do program in C, a lot of
the time.  Been doing it for years.  I'm not attacking C, I'm attacking the
thoughtless use of C in applications where it doesn't belong.  C is a fine
language for a lot of things, but some people seem to be operating on the
age old principle,
  "If all you have is a hammer, everything looks like a nail."

-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona

gwyn@brl-smoke.ARPA (Doug Gwyn ) (04/26/87)

I tried to ignore this debate, but here goes:

In article <682@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
>Well, let's see.  There are some obvious ones, like real-time life-support
>or safety-monitor applications in which it is absolutely necessary to show
>that in the worst case, the response time will be less than x microseconds.
>(This requires not only assembler language, but also relatively primitive
>CPUs and system designs in which the hardware performance can be determined
>exactly, none of this "depends on the percent of cache hits" stuff allowed.)

While sufficient, that approach is not necessary.  I know of several
real-time applications coded in a variety of high-level languages that
work just fine.  One just has to know what one is doing.

>Less obviously, almost anything having to do with character handling.  Most
>CPUs provide hardware instructions for string copies, compares, and searches.
>C provides no statements which could be compiled into these instructions.

This also is not correct.  C these days includes a standard run-time
support library with a substantial number of string-handling routines;
most reasonable implementations make use of special machine instructions
when they exist (and when they are faster than ordinary instructions,
which amazingly is not always the case).  It is even possible to avoid
function call overhead and generate in-line code for these routines
under most circumstances, although generally it's not worthwhile to do so.

>Also, anything that has to do with direct hardware manipulation (like device
>drivers).

There may occasionally be problems, but I (among others) have written
device drivers that port between radically different machine types with
several orders of magnitude less work than would be required for assembly-
language versions, and performance is quite acceptable.  Virtually all
UNIX device drivers are written in C.  I even find C an easier way to
access device registers on my Apple (since such access is only a small
fraction of the necessary operations for most device-handling algorithms).

>Any programmer who has only PCC available should *not* believe
>any of the jive about C code being efficient.

Most of us learned long ago that there are better ways to spend our
time than trying to squeeze every last drop of performance out of a
particular system.  There are several kinds of "efficiency", including
"programmer efficiency".

It's certainly true that C is not always the best choice for every
programming project.  However, it is usually a pretty good one, and
if one has to concentrate on a single language for a random
assortment of projects, C is probably a better choice than any other
language.  Some relevant considerations are availability, portability,
efficiency, closeness to the machine, and so on.  In spite of much
experience in programming in a variety of assembly languages, I do so
now only when absolutely necessary (such as in creating run-time support
for C compilers).  It just isn't good economics to do otherwise.

I don't quite understand the argument about C programmers being less
skilled than for other languages.  It takes MORE skill to be really
proficient in C than in languages such as Fortran and BASIC.  If you
mean that home hobbyists tend to be less than professional, and that
they often use C (or BASIC!), then that's probably true.  So what?
Incompetents will do a much worse job using assembler, and skilled
professionals will do a better job using C.

manis@ubc-cs.UUCP (Vincent Manis) (04/26/87)

In article <682@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes, quoting me:
>> I'm certainly not a C zealot, but I can't think of *any* application for
>> which C is a poor choice as a language but assembler is a good one.
>Well, let's see.  There are some obvious ones, like real-time life-support
>or safety-monitor applications in which it is absolutely necessary to show
>that in the worst case, the response time will be less than x microseconds.
>(This requires not only assembler language, but also relatively primitive
>CPUs and system designs in which the hardware performance can be determined
>exactly, none of this "depends on the percent of cache hits" stuff allowed.

I said *language*, not *compiler*. It may well be that on a particular
machine a particular problem requires the use of assembly language. But
there is nothing in the specification of C (or other high-level languages)
which mandates they be less efficient. In fact, it is often the case that
the use of a high-level language makes possible the implementation of
substantially more efficient algorithms than would be reasonable for an
assembler programmer.

>> Of course, that does not mean that a particular C compiler will give code of
>> adequate efficiency.
>
>This is the more important consideration.  There's a lot of talk about what
>compilers *could* do in optimization.  And somehow that's gotten translated
>into the mistaken belief that all of this wonderful optimization is really
>happening, or will happen soon.
In 1973, when UNIX was rewritten in C, the estimate was that the new system
was about 1/3 larger (in space) than the old, though the new system was
substantially more powerful. Compilers have gotten a lot smarter since then.

>The truth is that most C compilers are based on PCC, and produce absolutely
>terrible code.  Any programmer who has only PCC available should *not* believe
>any of the jive about C code being efficient.
Agreed. pcc is not my idea of a good compiler. But we were discussing the
language.
>And even with the decent optimizing compilers, code size in C is awful.  See
>the discussion in comp.sys.amiga about how to get "hello, world" down from
>8K to 1K!  In assembler, it'd be maybe 50 bytes.
I'm not an Amiga expert. However, I hope that Doug isn't labelling Lattice C
as a decent optimisisng compiler.

>Don't get lost, I'm getting to a subtle point (I hope).  Because there are
>so many C programmers, who will work for low pay under less-than-thrilling
>conditions, many employers insist that their employees write in C.
If I were a programming manager, I would insist that employees write in a
high-level language. Whether this language is C, Modula-2, Pascal, or even
(ugh) Ada is not the point. For reasons of efficiency, reliability, and
portability, there is simply no alternative. (BTW, employers who want less
than the best so they can pay less than the best generally don't last too
long, at least in my experience).
-----
Vincent Manis                {seismo,uw-beaver}!ubc-vision!ubc-cs!manis
Dept. of Computer Science    manis@cs.ubc.cdn
Univ. of British Columbia    manis%ubc.csnet@csnet-relay.arpa  
Vancouver, B.C. V6T 1W5      manis@ubc.csnet
(604) 228-6770 or 228-3061

"Long live the ideals of Marxism-Lennonism! May the thoughts of Groucho
 and John guide us in word, thought, and deed!"

jbuck@epimass.UUCP (Joe Buck) (04/26/87)

In article <682@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
>> I'm certainly not a C zealot, but I can't think of *any* application for
>> which C is a poor choice as a language but assembler is a good one.
>
>Well, let's see.  There are some obvious ones, like real-time life-support
>or safety-monitor applications in which it is absolutely necessary to show
>that in the worst case, the response time will be less than x microseconds.
>(This requires not only assembler language, but also relatively primitive
>CPUs and system designs in which the hardware performance can be determined
>exactly, none of this "depends on the percent of cache hits" stuff allowed.)

Granted.  But then you can't use any modern CPU chip, without
overspecifying CPU power a good bit.

>Less obviously, almost anything having to do with character handling.  Most
>CPUs provide hardware instructions for string copies, compares, and searches.
>C provides no statements which could be compiled into these instructions.

Not granted.  Library functions (strcpy, strcmp, etc) should be
written in assembler to best exploit a particular machine's
capabilities (though one can start the project with a C version).
ANSI C will provide mechanisms to compile this in-line.

>Also, anything that has to do with direct hardware manipulation (like device
>drivers).  On the 68000, for instance, the assembler programmer has the
>MOVEP instruction which allows easy writing of multiple bytes to I/O
>devices.  In C, there's no statement that can do this, so you end up with
>  *(char *)0x12345 = (val >> 8) & 0xFF;
>  *(char *)0x12347 = val & 0xFF;

The 68000's notion of "odd bytes only" or "even bytes only" for device
registers is ugly enough so you have to use assembler.  Gag me!

>Another reason for writing device drivers in assembler is to have absolute
>control over when and how the I/O locations are referenced.  Trying to
>program an ordinary 2681/68681 DUART, for instance, you have to have a certain
>amount of time pass between references.  And certain locations are off-limits;
>you don't want the C compiler generating a >1 byte fetch which accidently
>includes a no-no byte (this is with memory-mapped I/O, of course).

This is an argument for using a mixture of assembly and C.

>And even with the decent optimizing compilers, code size in C is awful.  See
>the discussion in comp.sys.amiga about how to get "hello, world" down from
>8K to 1K!  In assembler, it'd be maybe 50 bytes.

Wrong, since "printf" is written in assembler in most cases.  I keep
seeing this absolutely bogus argument.  The right solution is 

	puts ("Hello, world\n");

which should be about 50 bytes.  You should know better, Doug. printf
contains code to handle every possible %n.m{s,d,c,f,o,x} that might
appear.  That's a lot of stuff to drag around when you don't need it.

>I hope that isn't how it sounded.  What I was trying to say is that C
>programming is a rather attractive profession to someone who isn't really a
>professional-level programmer, and it is just too easy for them to get into.
>The existence of these people drags down the average quality of C code, and
>puts heavy strain on salaries and working conditions for *all* C programmers.

Oh, crap.  I don't think the quality of most assembly code I've run
into is really all that high.  As Sturgeon said, 90% of everything is
crap.  A good programmer will produce n lines of working C just as
fast as n lines of working assembler.  A good programmer will also
recognize that the best solution to some of the problems you pose is
a mixture of assembler and C, and the more C there is, the fewer bugs
the code will have.

>Don't get lost, I'm getting to a subtle point (I hope).  Because there are
>so many C programmers, who will work for low pay under less-than-thrilling
>conditions, many employers insist that their employees write in C.

Because C can be produced faster, is easier to debug, and, yes, can
be done by more people; because C can be written to be portable when
done by a knowledgeable programmer, many employers specify C.

>Subpoint #1: This really is the employer's right. Subpoint #2: This guarantees
>that they can get cheap replacements with little effort.  Subpoint #3: The
>employers don't really have to insist too hard, because the "programmers" they
>hire don't know anything but C anyway.  Subpoint #4: This is a great
>situation for those not-too-competent programmers; even though the pay is low
>and there's a lot of competition for the jobs, things are a lot worse in the
>burger-flipping business.

I find that people who know too much about the machines they are
working on produce worse C!  Why?  For example, picture a 68000 C
with 2-byte ints and 4-byte longs.  I had a guy who insisted on using
longs instead of pointers.  Well, it works, and it's the right size,
right?  People who are too close to the hardware often tend to build
unstructured monstrosities, which, while often fast and small, remind
one of the old joke: if architects built buildings the way
programmers built programs, the first woodpecker that came along
would destroy civilization.

Because of the freedom C gives you, it can be a dangerous language
for a poor programmer.  Really good C programmers are a rare breed
and deserve good pay.

>Major Point: what is good for the employer and for the not-so-capable
>programmers is not necessarily in the best interests of genuinely capable
>programmers.

I'm not convinced that the people you're calling "genuinely capable
programmers" are the same group I would use the term for.  How about
"overspecialized has-beens"?  For many problems, people-time is more
expensive, and software maintenance more costly, than some waste in
CPU time and memory space.  So the C program is twice as big and half
as fast as the best possible assembly program.  It can be produced in
one-third the time, and it's cheaper to maintain, and it's more
likely to be correct.  And, if done correctly, it can be moved to
different hardware quickly and cheaply.  If anything, C gives you too
much freedom and a more restrictive language might be still better.  

>Corollaries: it is also not necessarily in the product's best interests.  It
>is also not necessarily in the customers' best interests (for those cases when
>the code is part of a product).

Certain applications have to be fast and small, and times have to be
predicted exactly.  For these applications, use assembler.

>  "If all you have is a hammer, everything looks like a nail."

While you say you use C sometimes, I think assembler is your hammer.
I think the group of problems where assembler is appropriate is a
good deal smaller than you think it is.
-- 
- Joe Buck    {hplabs,ihnp4,sun,ames}!oliveb!epimass!jbuck
	      seismo!epiwrl!epimass!jbuck

bzs@bu-cs.BU.EDU (Barry Shein) (04/27/87)

Posting-Front-End: GNU Emacs 18.41.4 of Mon Mar 23 1987 on bu-cs (berkeley-unix)



Everyone, in retort to Doug Pardee's note about character handling in
C vs Asm mentions library routines (and possibly inline asm.)

How about the now well established structure assignment:

struct string {
	char thechars[STRSIZE];
};

If I declare s1 and s2 to be struct strings and do a:

	s1 = s2;

my Vax generates a movc3, my Encore (NS32K) generates a MOVSD
instruction, my Sun (68K) generates a tight dbra loop and my IBM3090
generates a MVCL, all of that is quite optimal if you want to save
yourself a trip to the prin-ops.

I think the problem is (if there is one) that most (all?) machine
architectures don't have a lot to deal with null term'd strings. Oh,
you can use a LOCC on a Vax or even a TRT on an IBM (370, not PC),
minor, but most assembler programmers will go for fixed length string
formats that will match the hardware (not a slur, it's the right thing
if you're after raw speed and can stand some loss in flexibility.)

I can't argue with the success of C's null terminated string formats,
but I've coded in a BCPL string style in C programs to speed up certain
operations and I still have all 10 of my fingers.

It just doesn't often matter much (tho you want to profile strcpy()
when your text processing slows down, you might change your mind.)

	-Barry Shein, Boston University

mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) (04/27/87)

In article <682@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
>And even with the decent optimizing compilers, code size in C is awful.  See
>the discussion in comp.sys.amiga about how to get "hello, world" down from
>8K to 1K!  In assembler, it'd be maybe 50 bytes.
>
>-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona


You might make 50 bytes, Doug. But I'd be willing to bet against it.
There's more than 500 bytes of overhead in that C program that
re-arranges the environment to look like Unix. A lot of this you won't
be able to avoid. If you're not used to OS's that consist of
cooperating processes, some of it is liable to surprise you.

At one point in the past, a friend and I had a contest to produce the
shortest executable file to do a simple task (continously output a
string of C-G's to stdout) on a v6 Unix system. As I recall, it was a
draw. His carefully tailored assembler program, with the a.out header
hand-stripped from it, was the same size as my shell script - 17
bytes. Of course, I can produce a smaller shell script now.....

	<mike

--
Take a magic carpet to the olden days			Mike Meyer
To a mythical land where everybody lays			ucbvax!mwm
Around in the clouds in a happy daze			mwm@berkeley.edu
In Kizmiaz ... Kizmiaz					mwm@ucbjade.BITNET

doug@edge.UUCP (Doug Pardee) (04/27/87)

// Note to the net members:  I realize this discussion is getting pretty far
afield for a "C" discussion forum, and this posting doesn't help much.  I
rationalize it only in that it relates to when to use and when not to use C. //

I'd like to expand upon the comment

> ... The difficulty of writing good programs is the
> now unnecessary awkwardness of assembler constructs.  If an intelligently
> designed assembler, or even macro translator, were available, this could
> be done.

I kinda suspect this is one of the big reasons that many people think that
assembly programming is difficult: the assembler software they're using is
terrible.  In particular, most U*ix-based assemblers contain only the bare
minimum features needed in order to assemble the output of PCC.

Imagine if you had to use a C compiler which was designed only to compile
output from the FORTRAN compiler.  Six-character (all caps) variable names,
no char variables, no shifts or logical operations, no pointers, no unions
nor structures!!  And the coup de grace, *no preprocessor*.  If this
"brain-damaged" compiler was the only C compiler you'd ever seen, I wouldn't
be surprised if you thought that C was a difficult and cryptic language.

I have yet to see an assembler for a microprocessor which has the functionality
of mainframe assemblers written a quarter century ago.  The closest is the
Microsoft Macro Assembler for the PC, and it has the major deficiency of not
being able to deal with macro libraries (not to mention the fact that it
necessarily has to assemble iAPX86 code :-).  The best assembler translator
program that I've ever used was (cover your ears, kiddies, I'm gonna say a
naughty word) the IBM 360/370 OS assembler.  Just about everything you'd
ever want was in that assembler.  Not always in the nicest syntax, but at
least it was there.  But that assembler is 25 years old now!

-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona

doug@edge.UUCP (04/28/87)

// Note to the net:  This is getting out of hand.  I really *am* trying to
keep this to a minimum, and trying to avoid "is/is not/is so" arguments. //

I think I gave the wrong impression:

> > I'm certainly not a C zealot, but I can't think of *any* application for
> > which C is a poor choice as a language but assembler is a good one.
>
me>Well, let's see.  There are some obvious ones, like real-time life-support
me>or safety-monitor applications in which it is absolutely necessary to show
me>that in the worst case, the response time will be less than x microseconds.
> 
> I said *language*, not *compiler* ...
> there is nothing in the specification of C (or other high-level languages)
> which mandates they be less efficient.

I wasn't referring to efficiency.  I was referring to predictability.  I can
look in my book here and find that on a 10MHz 68000, the instruction
  MOVE.W 6(A0),D0    will take exactly 1.6+.1w microseconds, for a "w" wait
state memory.  I haven't the vaguest idea how long it will take to execute
  temp1 = ptr1->count;   and what's worse, a change to some other part of
the same module could cause this statement to be faster or slower (as the
result of global optimization).  Okay?

-- Doug Pardee -- Edge Computer Corp. -- Scottsdale, Arizona

amos@instable.UUCP (Amos Shapir) (04/29/87)

In article <685@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
>I kinda suspect this is one of the big reasons that many people think that
>assembly programming is difficult: the assembler software they're using is
>terrible.  In particular, most U*ix-based assemblers contain only the bare
>minimum features needed in order to assemble the output of PCC.
> ...
>I have yet to see an assembler for a microprocessor which has the functionality
>of mainframe assemblers written a quarter century ago.

So, people do not like assembler because they have no macros - ok, let's
give them  named macros to  name their constructs, and  macro libraries,
and why  not recursive macros so  they can use nested  IF/WHILE macros -
and Voila!  you have created an  almost HLL syntax, but  unlike a 'real'
HLL, the generated code is ugly and inefficient, and you do not have the
security of type checking.

The syntax of early dialects of C suggest that it also strated that way.
Then  it became  popular  and people  started to  port  it to  different
architectures.  This   required  stronger  definitions  than   just  the
'whatever  your hardware  does' of  early  C, adding  more keywords  and
constructs to the  language. Now we have come around  a full circle, and
people want a 'back to earth'  small language (as is evident by articles
describing the SPL language in comp.lang.misc yesterday, or the 800-line
FOOGOL compiler  posted to net.sources last  month). Is that the  end of
carefree C as we know it?
-- 
	Amos Shapir
National Semiconductor (Israel)
6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel  Tel. (972)52-522261
amos%nsta@nsc.com {hplabs,pyramid,sun,decwrl} 34.48'E 32.10'N

hrubin@osupyr.UUCP (Herman Rubin) (05/05/87)

In article <757@instable.UUCP>, amos@instable.UUCP (Amos Shapir) writes:
> In article <685@edge.UUCP> doug@edge.UUCP (Doug Pardee) writes:
> >I kinda suspect this is one of the big reasons that many people think that
> >assembly programming is difficult: the assembler software they're using is
> >terrible.
> 
> So, people do not like assembler because they have no macros - ok, let's
> give them  named macros to  name their constructs, and  macro libraries,
> and why  not recursive macros so  they can use nested  IF/WHILE macros -
> and Voila!  you have created an  almost HLL syntax, but  unlike a 'real'
> HLL, the generated code is ugly and inefficient, and you do not have the
> security of type checking.
> 
> 	Amos Shapir
> National Semiconductor (Israel)
> 6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel  Tel. (972)52-522261
> amos%nsta@nsc.com {hplabs,pyramid,sun,decwrl} 34.48'E 32.10'N

Both of these posters miss the point.  I do not object to typing, but I do
to strong typing.  The macro processors that I have seen are inadequate.
I would like     x = y - z   to be a macro, where the translation into the
appropriate code would depend on the types of x, y, and z.  However, I should 
be able to override this if I, the person who knows what I am trying to do,
think it appropriate.  I want to be able to be able to use such constructs as
multiple words, multiple word shifts, condition codes (including overflows),
etc.  I may even have to introduce additional macros, such as a triple word
shift or an alternative to it on machines which require even-odd register pairs.
I should be able to specify the k-th most significant word or byte of a
multi-unit construct without having to worry whether the machine is little
or big endian when that can be done.  I know the benefits and dangers of
using boolean operations on floating point numbers, and know that machine
dependence is frequently necessary to get any efficiency.  

As for the generated code being ugly and/or inefficient, there are few cases
in which compilers have produced code which is any better than the way I would
code the problem.  I am not against compilers and HLLs provided they allow the
programmer to easily override the bad code they produce.  Also remember that
the cost of subroutines is high and the cost of transfers is not low; the 
cost of memory references is also not too good.  I think a fair compiler/
assembler can be produced.  I also believe that anyone who attempts to produce
a good language/compiler/assembler can only produce a bad product.  All mankind
does not know enough to restrict the intelligent programmer; the current 
attempts only brain-damage.
-- 
Herman Rubin
Until mid May:
Department of Statistics, Cockins Hall, 1958 Neil Avenue
Ohio State University, Columbus OH43210
hrubin@osupyr or cbosgd!osu-eddie!osupyr!hrubin or ts0474@ohstvma.bitnet
"Permanent":
Department of Statistics, Purdue University, West Lafayette IN47907
hrubin@l.cc.purdue.edu or ihnp4!pur-ee!stat-l!hrubin or hrubin@purccvm.bitnet