Fast and BASIC (PC World September 1986 by Greg Perry) You won't start an argument by contending that BASIC has become the computer language of the masses. You might get a few takers, however, if you insist that it made it on its merits, with no help from firmware. If you held your ground, you'd best your opposition. BASIC represents an unusually successful attempt to be many things to many people. It's comprehensive yet accessible, boasting features that even some mainframe languages lack. Although BASIC has its inelegant, unstructured side, it's nonetheless easy to use. It relies on straightforward English statements -- such as PRINT and INPUT -- but is capable of complex operations, including creating and updating both sequential and random access data files. Like most BASIC users, you probably tend to write programs as fast as you can -- but you may well be bypassing ways to write the fastest programs possible. BASIC veterans and fledgling programmers alike can profit from techniques that build programs for speed. Computers cannot, of course, understand BASIC or any other high- level language directly. Their lexicon is limited to machine language instructions, encoded as 1s and 0s. Fortunately, because human beings make the machines (and the rules), there's no compulsion to convey commands as strange, symbolic representations of bits and bytes. Thus, high-level languages use instructions that mere mortals can read and save a ASCII files. A single instruction in a high-level language represents a series of elemental machine tasks. Interpreters and compilers enter the scene as agents that translate our instructions into commands the computer understands. Interpreted BASIC is a language of immediate gratification. Commands execute without compilation or linkages, and programs, once loaded, execute immediately. That's the case because interpreted BASIC programs are translated as they run. An interpreter reads a line of code, translates it to machine instructions, and executes those instructions. It then reads, translates, and executes the next logical line in the program, even if it has processed that line before (for example, in a loop of instructions). A compiler, on the other hand, reads and translates a BASIC program as an entity, writing to disk an entirely new file called an object code file. The object code file is then linked to other compiled program modules with a special program called a linker. Even if you have no other compiled program modules, you must use the linker to create a third disk file, the executable (.EXE) file. While the compilation process is time-consuming, it generates code that executes in a flash; interpretive code, by contrast, is ready to run and can be edited easily and quickly, but executes slowly. Most BASICs (including the PC's) are interpreted rather than compiled. While interpreted BASIC programs may be compiled, compilers are generally used only by professional programmers. Interpreters have become a hit with novice programmers because interpreted BASIC programs are easy to tinker with and rerun. That interactive environment is conducive to understanding and experimentation. The trade-off is execution speed: A compiled programs is several orders of magnitude faster than an interpreted one. If SAMPLE1.BAS were to be compiled, for example, the entire program would be read and (except the REMarks in lines 5 and 15) translated into object code and stored on disk. (The compiler acknowledges the REMark lines only once and doesn't bother to compile any such lines of code.) The object code would then be linked to produce an .EXE file. SAMPLE1.BAS: 5 REM *** Program to print the numbers 1 through 10 10 I=1 15 REM --- Print the number 20 PRINT I 30 IF I>=10 THEN 60 40 I=I+1 50 GOTO 15 60 END In contrast, if the program were run through an interpreter, each line would be individually translated and executed. Even REMarks are first identified and then ignored every time program execution loops through a REMark line. SAMPLE2.BAS requires some 500 interpretations of the REM statements within its loop. It's no wonder that interpreted BASIC incurs major speed penalties. But once you appreciate the strengths and limitations of interpreters, it's possible to optimize interpreted BASIC programs. SAMPLE2.BAS: 5 REM *** Interpreting REMarks slows 6 REM *** down BASIC programs 10 DEFINT I 20 FOR I=1 TO 100 25 REM --- This REMark takes up time 26 REM --- So does this one 27 REM arkable, isn't it? 30 PRINT I 35 REM -- We are almost done 36 REM -- with the REMarks 40 NEXT I 50 END Obviously, eradicating all REM statements speeds program execution. Good documentation, however, is vital to your ability to understand and modify a program. So in lieu of banishing REMs completely, you can purge them from a run-time version of your handiwork. Documentation should be written as the program is developed, not afterward, and a thoroughly REMarked-up version of the program will serve as a permanent reference. In a nutshell: When writing a BASIC program, use REMarks liberally; once the program is written and debugged, copy it (giving it a different name) and eliminate the REMs. BASIC Programming Tips to Live By: - Delete REM statements from your code - Declare all variables with a DEFtype statement before the variables are used - Keep variable names short - Use variables instead of numeric or string constants - Place DATA statements at the beginning of a program - Put frequently called subroutines near the beginning of a program - Use the colon to combine as many statements per program line as you can - Eliminate all unnecessary spaces - Don't use extraneous words in statements Our quest for efficiency also entails declaring all variables as integer, single-precision, double-precision, or string variables by means of DEF statements at the start of the program. For integer routines, failure to declare a variable's type has the unhappy consequence of causing numeric variables to default to single-precision variables. In such a situation, every time a FOR...NEXT loop is interpreted, for example, the index variable (such as the variable I in FOR I = 1 TO 10) would be carried out to six decimal places -- a needless squandering or processing time. Some programmers prefer to declare a variable's type explicitly when it's used, rather than relying on the global declaration of a DEF statement. You could, for instance, declare I as an integer by following it with the percent symbol at every occurrence in the program. Although this approach beats letting the variable default to single-precision, you're still asking the computer to spend clock ticks interpreting I%. Program execution is quicker if you declare I as an integer-precision variable with the DEFINT I statement. With string variables, even die-hard, speed-conscious BASIC programmers tend to use H$ rather than declaring H as a string in a single DEFSTR H statement. Like the I% approach, however, H$ exacts its price in slower execution. Furthermore, although IBM BASIC permits variable names of up to 40 characters, the shorter the name, the more efficient the interpretation. The drawback of brevity, however, is that the code is less informative. Each time your program uses a numeric constant, you force BASIC to add steps to its interpretive dance. By examining the ASCII representation of each digit, sign, and decimal point, the interpreter converts the constant to the special format BASIC uses to store numeric values. Every time the interpreter encounters a numeric constant, the dance begins anew. You can forego these gyrations by assigning a constant to a variable and using that variable throughout the program. Once the number is converted and assignment made, BASIC simply looks up the variable's value in an internal table. For an example of this, compare SAMPLE3.BAS and SAMPLE4.BAS; the latter routine shows that pi is better handled as the variable PI than as the value 3.14159. SAMPLE3.BAS: 5 REM *** Interpreting numeric constants slows down BASIC programs 10 DEFINT I 20 FOR I=1 TO 100 30 PRINT 3.14159*I 40 NEXT I 50 END 5 REM ** Using a variable in place of a constant speeds up BASIC progs 10 DEFINT I 20 PI=3.14159 30 FOR I=1 TO 100 40 PRINT PI*I 50 NEXT I 60 END You can squeeze similar speed improvements from your code when working with string constants. Assigning such a constant to a variable once -- and henceforth using that variable in place of the constant -- shortens the program and eliminates multiple interpretations. Arranging statements in a sensible order is often the soul of an efficient program. Any time you use a set of READ and DATA statements, put the DATA statements as close to the top of the program as possible. When a READ statement is executed, the interpreter searches the program from the beginning to find the next unused DATA line, even though the DATA statements may directly follow the READ statements. Likewise, when a program reaches a GOSUB, the BASIC interpreter goes to the beginning of the program and reads line numbers until it hits the subroutine's line number. BASIC simply isn't capable of proceeding directly to the line of code called. Therefore, it's a good idea to place the most frequently used subroutines ahead of routines you use less often. In doing so, you painlessly minimize BASIC's work when a program reaches a GOSUB. Obvious as it seems, this simple procedure is usually overlooked. Presentation of BASIC staetments can also bolster program performance. You can pack several BASIC commands into a single logical line (a maximum of 255 characters) by separating them with a colon. SAMPLE5.BAS, for instance, shows a seven-line program; SAMPLE6.BAS shows the same program shortened to just two lines. The second arrangement yields the same result as the first but does it faster, simply because its statements have been combined. With the colon carrying the load, the interpreter no longer needs to decode line numbers. SAMPLE5.BAS: 5 REM *** This program is big and thus slow 10 CLS:PRINT "Hello." 20 PRINT 30 PRINT "Who are you"; 40 INPUT N$ 50 PRINT "Well, "N$", I'm pleased to meet you." 60 END SAMPLE6.BAS: 5 REM ** This program is more compact and thus faster 10 CLS:PRINT "Hello.":PRINT:PRINT "Who are you";:INPUT N$:PRINT "Well, "N$", I'm pleased to meet you.":END Granted, the two-line program is a bit hard to decipher (which is why your original need not be this compact), but it leaves the seven- line version at the starting gate. Note also that the two-liner doesn't contain a single superfluous space. Spaces can't be eliminated entirely. You'll need them (or other delimiters) to separate BASIC commands and variable names. For instance, the interpreter does not understand PRINTI to be PRINT I. Nor can you jettison spaces from string constants. Assignment statements, however, should read something like A=B+C instead of A = B + C. Wiping out spaces may seem trivial, but the interpreter treats spaces just as it does alphanumeric symbols. As a rule, the fewer spaces, the better. On the whole, BASIC programmers are savvy enough to avoid inserting redundant words and commands. Most have been omitting the redundant LET statement for so long they've progably forgotten LET exists. Obviously, LET A=B is slower and less compact than A=B, which does the same thing. On the other hand, a few less obvious BASIC statements -- like OPEN -- also lend themselves to shortcuts. OPEN O,1,filename can do the work of OPEN filename FOR OUTPUT AS #1 LEN=128 in less than half the space. This litany of tips is just the beginning of resourceful, efficient programming. Most of the code you'll be generating in this way isn't pretty and doesn't make for especially readable listings. But the only thing likely to slow you down is the edict that you keep both sleek and well-documented versions of your programs.