cik@l.cc.purdue.edu (Herman Rubin) (04/16/88)
In article <3470@bunker.UUCP>, garys@bunker.UUCP (Gary M. Samuelson) writes: > In article <748@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes: > > > >Here is a challenge to the denigrators of goto. > > I am not sure I qualify, absolutely, but how about: > > Case 5: b >>= g16; > m |= b; > x = *(--geom); > if (TEST) > { > if(ODD(x)) > Case2(); > else > Case1(); > } > else > { > g4 = (x+1)/2; > if(ODD(x)) > Case3(); > else > Case4(); > } > > Readability improved through indentation at no extra charge. > Extraneous semi-colons tossed also. And, yes, I normally do > such things when I have to deal with ugly code. > > Now give me something hard. > > Gary Samuelson --For those who have not seen the original posting, replace Casej() by goto Casej. --------- I have no qualms with using or not using indentations. Also I think that the initial use of semi-colons by C was a mistake, and rather than getting obscure error messages, I admit I use too many. Now to the main point. I stated that this was a _fragment_ of code. After leaving Case5, this part of the code is not reached again until entered in the generation of a new item. That is, after going to one of the other cases, this block of code is _not_ to be returned to. Also, the cost of a subroutine call is huge compared with the cost of a goto. One of those responding by mail correctly deduced the original procedure, which can easily be written without gotos. The actual procedure as I would code it uses gotos even more, as it can be recognized that certain information may be in a reusable form. For example, instead of *(geom++) = z; in one case, followed by reading the item in the transferred case, just goto the intermediate step. The idea of the whole section of code, in a non-goto format, with most of the details omitted, as follows: (Please ignore C grammatical errors.) {if(w==3) {i=3; g4=1; break} else if (EVEN(w)){....} else if(w==5)ABORT; else .....} while(i>1) {switch(i) case 2: b >>= *(--geom); m |= b; i=1; break; case 3: (details omitted) case 4: (details omitted) case 5: as in the posted code, except goto Casej is replaced by i = j; break; case 6: (details omitted) case 7: (details omitted) default: (details omitted)} termination code (details omitted) A simple analysis of the instructions generated shows that much of the time is spent in storing the value of i (even if you can get your compiler to put it into a register) and carrying out the switch comparisons. In addition, there are many ways to combine operations using the structure of the code, and knowledge of the algorithm, to eliminate unnecesary transfers. The goto-less code has at least twice as many machine goto instructions as the goto code for the transfers to casej, and each there is an additional comparison instruction for each case move. I deliberately eliminated the storage of i for i<8. When your optimizing compiler can convert case switches with case numbers generated into the more efficient code such as I would write, I will consider eliminating those gotos. Alternatively, those features may be added to the language. However, I cannot see how even the most brilliant optimizing compiler could see some of the other uses of goto in the algorithm. There seems to be far more that the algorithm producer can think of than the language designer realizes is pos
peter@athena.mit.edu (Peter J Desnoyers) (04/17/88)
In article <751@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes: >When your optimizing compiler can convert case switches with case numbers >generated into the more efficient code such as I would write, I will consider >eliminating those gotos. I don't know about optimizing compilers, but I've heard horror stories of non-optimizing compilers using jumptables for switch statements. (Oh no!! Why does "switch (n) {case 0: foo(); case 20000: bar();}" generate an 80k executable?) It may have been an older VMS compiler. I dunno... Personally, I think that doing such grevious harm to the source code in the name of 'optimizing' is off the mark. The time spent writing and maintaining that code could have been spent (1) buying an optimizing compiler; (2) writing it in assembler; or (3) convincing your boss to spring for a faster machine. Perhaps a better thing than docking people's pay for 'goto's would be a compiler that put a delay in with every goto. Ten or twenty nop's should be about right. Then people would only use goto when it was really needed for stylistic reasons. btw, my favorite reason for using goto - to cope with the problem that the same word (break) is used to terminate loops and to signal the end of a section of a switch. Thus you can write "if (foo) break;", but you may have reason to write: (what you want to write: ) while (foo) while (foo) switch (bar) switch (bar) { { case n: case n: goto (end_while); break; break; esac; /* or whatever */ } } end_while: Peter Desnoyers peter@athena.mit.edu