Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Getting down to basics


JUL88: EXAMINING ROOM

Bruce Tonkin develops and sells software for TRS-80 and MS-DOS/PC-DOS computers, he writes mostly in Basic. However, he also writes some software in C and the assembly language. You may reach him at TNT Software Inc., 34069 Hainesville Rd., Round Lake, IL 60073.


If C is a scalpel, Basic is a Swiss Army knife. While some languages are more specialized---C for systems programming, APL for tables and array---Basic is more general. Rather than asking "Why Basic?", it might be better to ask "When Basic?"

Admittedly, no computer language is a universal tool. Like any other language, Basic has its own fundamental strengths and weaknesses. Also, each vendor's implementation of the language has its own particular strengths and weaknesses.

So what is it that makes Basic so basic? For one thing, it's familiar and readable. Nearly anyone can read a Basic program (though poorly structured code can make you wish you hadn't). Except for Applesoft (which lacks even an ELSE), spaghetti code is always the programmer's fault. Basic programs are often written by nonprogrammers precisely because Basic is so approachable. Pascal and C are harder to learn and less forgiving.

While I don't claim to speak for everybody, I can't easily read Pascal programs. They're counter-intuitive: all the procedures come first, then the main logic. I don't think that way, and I suspect most people don't. Such ordering can be overridden, but it's another example of a language written for the compiler-writer instead of for the user.

The pointer indirection in C is powerful, but ultimately even more confusing than spaghetti code full of GOTOs and GOSUBs. Of the other common languages for micros, Fortran, APL, and Forth are generally hard (or downright impossible) to read without significant study. None of these supports the range of built-in features Basic does. Basic is widely known, so it's easier to find a Basic programmer and it generally costs less to hire one-an advantage for users. Since Basic has so many things built-in, Basic programmers can be more productive, and that's an advantage for the programmer.

Basic (throughout, I refer to MS-DOS GW Basic) is good at string operations. C and Pascal handle strings with user-written or library functions and have no intrinsic string type. Basic's string garbage collection has drawn fire, but dynamic strings in other languages are nontrivial and slower than compiled Basic. Many applications require dynamic strings for efficient use of memory.

Basic has excellent ways of addressing the screen, drawing shapes, and using colors. Basic supports the communications ports, can easily append data to files, and has device independence for most input and output operations. Some versions of C and Pascal have such extensions, but many must be purchased as add-ons or must be written by the user. As a developer, I find that requirement annoying, non-portable, time-wasting, and expensive. I quit using C because I saw no way I could write better or faster routines than Microsoft gave me in Basic. I have no desire to reinvent the wheel.

Another of Basic's strengths is that it's a de facto standard rather than a standard de jure. The ANSI standard for Basic is widely ignored, since ANSI Basic foolishly resembles only itself. The real standard has been, and still is, Microsoft GW Basic, ANSI notwithstanding.

Critics have often dismissed Basic as "good only for quick and dirty jobs." Those critics remember 1976-style interpreted Basic (usually Applesoft). Basic has changed a lot since 1976, and Applesoft was so bad it probably moved more programmers to Pascal than did Borland.

Previous Basics did have problems: mandatory line numbers and no line labels; single-line functions; a clumsy interface to other languages; limited data types; and no multiline conditional statements. Those disadvantages made it difficult to write modular code, and often made debugging a nightmare.

Newer Basics make it easier to write structured code. Nearly every former disadvantage has been overcome. Still, these new versions have retained the built-in features that have made Basic so appealing.

The New Basics

The products compared here include Microsoft's MB 6.0, Quick Basics 3.0 and 4.0, and Borland's Turbo Basic 1.1. Though it's no longer being shipped by Microsoft, I decided to include Quick Basic 3.0 in this comparison for three reasons: first, because it and Turbo Basic came out at about the same time and the differences between Quick Basic and Turbo Basic are interesting; second, because Quick Basic is in some ways still the fastest Basic compiler for floating-point math speed; and third, because the environment for Quick Basic is much different than the later Microsoft products.

Any computer language is good only to the extent that it is useful. Therefore, I'll first rate these Basics on their general utility: how easily and how well they lend themselves to general-purpose business programming. Following that, I'll provide a comparative rating based upon the following criteria: speed, size of generated code, reliability, interface to other languages, portability, and environment.

In this analysis, I'll assume that any acceptable Basic will include capabilities, commands, and functions of GW Basic as a proper subset. Keep in mind the Swiss Army knife analogy. Every developer and every user is unique, and it's important that Basic serves all users well. Some of these Basics do a better job of that than others.

The competition between Borland, Microsoft, and others is leading to steadily more capable Basics. Standardized languages cannot evolve as fast. Users may demand dynamic strings and random files for Pascal, but I can't imagine ANSI adding them.

Quick Basic 3.0

Quick Basic (QB) 3.0 goes beyond GW Basic in a number of areas. The file and device I/O is essentially the same, but the maximum string length of 32,767 bytes makes it easier to read, write, and manipulate large chunks of data.

QB 3.0 supports the 8087 numeric coprocessor. For calculation-intensive programs, that's an enormous advantage. Most machines are not equipped with an 8087, though, and that creates problems. Programs can be compiled to use the 8087 and emulate it otherwise, but such programs run more slowly on machines without an 8087. The reverse is also true. Programs compiled without the emulation run well on machines without an 8087, but achieve no increased speed on machines with one.

Worse, the 8087 and non-8087 versions of these programs use a different format when storing numbers to disk. The IEEE format cannot be used by old-style programs, but either kind of program can use or write old-style floating-point numbers. So, most programmers avoid the 8087 library and stay with the old-style numbers, especially if a significant amount of data is in the old format.

Though individual numeric arrays may not exceed 64K in size, the programmer can use all available memory for numeric data. This can be an enormous advantage. Older Basics required that all such data share a single 64K segment.

QB 3.0 does not require line numbers, but error routines can identity locations only by reference to the last line number executed. This may force programmers to use unclear numbered references.

For control structures, QB 3.0 adds a DO loop with WHILE and UNTIL terminators, an EXIT statement for FOR and DO loops, and SELECT CASE. Block IF conditionals may include an ELSEIF.

A CONST (constant) declaration makes code easier to maintain and alter. Unfortunately, that declaration does not work like the C #define directive: CONST definitions do not apply outside the main module and may not include anything but the definition of a single variable.

QB 3.0 supports procedure-like subprograms, which can be separately compiled and collected into libraries. The subprograms may not be recursive, however, and the compiler does no argument type-checking or conversions. Both of these factors cause subtle and hard-to-find bugs.

QB 3.0 does not allow useful changes to the stack size or the memory used. A CLEAR statement can adjust the stack, but using it erases all variables (including any passed in common). That's frustrating, since the default stack size is only 768 bytes. QB 3.0 programs always grab all available system memory.

DOS calls are supported with the CALL statement. In practice, that means DOS services will require a short assembler routine. Assembler is not difficult to use, and the QB manual includes several examples.

For utility, QB 3.0 far surpasses GW Basic. The code can be more modular, and the language enhancements are well-suited to business programming.

Turbo Basic 1.1

Turbo Basic (TB) is much like QB 3.0, but adds interesting and valuable capabilities. As with QB 3.0, the maximum string length is 32,767 bytes. TB also adds binary file I/O. That is a powerful enhancement, and especially useful for reading "foreign" data files, such as those created by dBase. To complement this binary mode, TB adds a SEEK function (as in C) to position the file pointer or determine location.

The example of binary file I/O shown in the TB manual is in classic backward Pascal style. Subprograms and functions are defined first, and only the last few lines of the program use SEEK. Programmers comfortable with that style can use it in TB (or in QB 3.0).

TB also adds a useful long integer data type. Long integers are clearly useful for binary files, but are also good for exact dollars-and-cents arithmetic with an implied decimal point. Such arithmetic should be far faster than BCD math and equally accurate (though with a smaller range).

Another major difference between QB and TB is in numeric processing. TB will always use an 8087 if present and emulate it otherwise. That simplifies things somewhat, but makes for problems.

TB is slower at floating-point math than QB 3.0 programs compiled without 8087 support. Since few MS-DOS computers have an 8087, TB will perform floating-point math much more slowly than QB 3.0 can for most users.

TB will allow the programmer to use all available memory with arrays of up to 64K each. This is identical to QB 3.0.

TB handles line numbers and errors like QB 3.0, with the same disadvantages. TB does have one advantage in event-trapping, though, which can be turned on or off for selected ranges of program lines. TB programs can gain a big speed advantage from this.

TB also allows the programmer to turn off range-checking. This can add speed, but out-of-bounds references can cause fatal crashes.

Control structures are the same as in QB 3.0, with DO and FOR loops (and EXIT commands to leave either loop prematurely), SELECT CASE, and multiline conditionals including ELSE IF. Here, TB differs from QB 3.0. TB will not permit anything on the same line as the ElSE in a multi-line IF, and QB 3.0 will. I find the QB 3.0 syntax more readable, especially for short statements. TB surpasses QB 3.0 in allowing EXIT for WHILE, SELECT, and block IF. TB also supports conditional compilation.

Many older Basics have implemented an increment or decrement operator. TB uses INC and DEC for this---INC A is less verbose only if the variable name is longer than one character.

TB allows constant declarations, but only for integer variables. That is less useful than QB 3.0.

TB allows recursive subprograms, but since TB does not support libraries or separate compilation, subprograms are less useful. As with QB 3.0, TB does no argument typechecking or automatic conversions on calls to subprograms.

TB will allow the programmer to set the amount of memory the program will use and the size of the stack (which is vital for recursion).

DOS services are easy to use in TB by using CALL INTERRUPT. Though using assembler routines is different than QB 3.0, it is not difficult. The CALL INTERRUPT statement makes the use of assembler less necessary as well.

TB allows COM files to be included directly, or as hex codes (inline). The inline approach enables the programmer to make small changes without re-assembly. For the kind of short routines often used in Basic, this makes a great deal of sense.

Still, Turbo Basic's lack of a link step means that it is more difficult to use code libraries than with QB 3.0. That is often a disadvantage.

For utility, TB is roughly equivalent to QB 3.0, with enough strengths to make it preferable for some operations. ln particular, binary file I/O and long integer arithmetic are substantial advantages. The CALL INTERRUPT is another strength. Recursion is not real important. The main disadvantages of TB are the floating-point math speed and the lack of linkable libraries.

Quick Basic 4.0

QB 4.0 is not QB 3.0 with the bugs fixed. Rather, QB 4.0 is a completely new Basic that is fundamentally different from previous versions. However, QB 3.0 programs will run under QB 4.0. Only the assembler routines must be altered.

Microsoft apparently responded to TB by taking the most popular features and including them in QB 4.0 for binary file I/O, long integers, and SEEK. The QB 4.0 syntax is slightly different for binary file I/O. TB uses GET$, and QB 4.0 uses GET. I prefer the Microsoft syntax.

Microsoft also dropped support for non-IEEE math. Like Borland, Microsoft added functions to allow QB 4.0 to use or to write old-style floating-point numbers. Microsoft's function names are different (and more difficult to remember), but otherwise work identically.

The slower IEEE math is a disadvantage for QB 4.0 as compared to QB 3.0. Some operations take more than 30 times as long to run. If math chips were cheap or present on most machines, I could accept this more easily. As it is, many applications may never be converted from QB 3.0 to QB 4.0.

QB 4.0 allows recursive subprograms, but the stack problem remains from QB 3.0: setting the size with CLEAR erases all existing variables. This means recursion cannot be used for many CHAINed programs.

The TB CALL INTERRUPT command has also been added, with minor differences in how the registers are specified.

QB 4.0 goes beyond TB and QB 3.0 in three main areas. First, arrays are not limited to 64K in size (except dynamic strings). Second and third, user-definable data types and a new "static string" data type have been added. The first enhancement is self-explanatory, but the others require discussion.

Static strings are nothing more than implicit arrays of type character. Static strings are really not strings, though they can be treated as such in programs. Such character arrays can be placed outside of normal string space, which makes them quite useful.

Static strings are converted into dynamic strings whenever they are modified, and then reconverted back. That's slow. Even operations that do not need to create dynamic strings (for example, assignment or MID$ replacement operations) will do so. This is disappointing, since a simple MOVSB instruction should perform the necessary operation quickly.

Static strings have another problem: their size must be declared at compile-time. If you attempt to declare a static string of length I%, where I% is determined when the program is run, you will get errors. Most often, QB 4.0 will tell you "invalid constant" and highlight the array name (not the offending constant) on the line where you attempted to DIM the static string or array. This forces sort and database utility programs to be hard-coded for each use.

User-defined types are similar to C structures and Pascal records. The programmer may use such a variable type to refer to any collection of fundamental data types except arrays. Many reviewers have been seduced by this similarity to Pascal records, and with reason---the Microsoft manual emphasizes it.

In practice, user-defined types are ill-suited to file I/O because such types may not include arrays. Consider a common application where a record contains 100 fields, half of which are repetitive floating-point numbers or integers (rates, prices, instrument readings, or similar quantities).

To modify those numbers within a user-defined type (set them all to zero, or add 10 to them, or discount them by 10 percent), QB 4.0 requires that each one be specifically named---loops are not possible. If the record were FIELDed in the old style, the update could be performed in a loop with a significant saving in program size.

Or suppose that one file must be copied to another with more, fewer, or re-ordered fields. With user-defined types, each element must be individually specified and copied by name. This is verbose. Worse, you have no easy way to write a program capable of selecting and transferring arbitrary fields at run-time.

The no-included-array restriction makes user-defined types nearly useless for managing blocks of record pointers and other more-advanced data structures, as well.

You may have an array of a user-defined type. There is a subtle bug that affects both these arrays and arrays of static strings. If an array of either kind is defined, that array cannot be larger than 128K in size, if the length of each element is not a power of two. This is true no matter how much memory is available. So, a program may possibly be unable to create an array of 1,025 127-byte strings, but successfully create an array of 3,000 128-byte strings.

Though they are not yet as useful as they might be, user-defined types are a valuable enhancement to Basic. Static strings can be quite useful,too. Single arrays larger than 64K are another obvious advantage. If not for the math problems, QB 4.0 would be clearly preferable to QB 3.0.

Microsoft MB 6.0

Microsoft Basic (MB) 6.0 is similar to QB 4.0, as it ought to be. An updated version of QB 4.0 is included in the package. MB 6.0 includes a set of subroutines that can make it possible to eliminate at least some unnecessary run-time support. That can make applications programs appreciably (up to about 20 percent) smaller than QB 4.0. More important, MB 6.0 will allow the programmer to use an alternate math library, regaining nearly all the speed lost in QB 4.0.

Besides QB 4.0, MB 6.0 also includes the CodeView debugger (the user-configurable Microsoft editor) a utility that can be used to create special versions of the various libraries, and a set of tools for creating OS/2 applications.

Both MB 6.0 and Quick Basic 4.0 use something that Microsoft calls "threaded P-code technology" and the company describes as "new." It is new---for Basic---but has been used for a long time for Forth and for some Pascal compiler.

With P-code, you can make small changes to a program easier and then resume execution, examine the values of variables or expressions, or even alter variables while debugging. Since only a small part of the program is changed, only a small part of the P-code is changed: Compilation (to P-code) is nearly instantaneous. In effect, debugging can proceed much as it might have for an interpreted program, but the P-code program can perform many operations nearly as fast as when truly compiled. No Basic interpreter was ever as powerful as QB 4.0, and none ever had the debugging facilities. The P-code is a substantial advantage to overall development.

P-code has an obvious advantage for Microsoft---it's highly portable. Much of QB 4.0's environment could be moved to another processor without change. Perhaps Microsoft will release QB for the 68000 or other processors. Even if something like that is not being planned, at least it is possible. With older compilers, such a product would be much more work. The only portion of the compiler that would need substantial change would be the actual code generation for executable files.

For utility, MB 6.0 is clearly better than QB 3.0, except in circumstances where math speed is absolutely essential and a math coprocessor chip will not be present. Even then, the penalty will depend to a great extent on the operations.

Ratings

Now I'll rate the different versions of Basic on criteria suited to any computer language. The first rating will be for speed.

Speed

For programmers, a compiler must perform its job quickly. If a program can be compiled rapidly, more time can be devoted to finding and fixing subtle bugs. Further, a fast compiler will make optimizing programs easier.

For users, speed simply means "how long does it take to ______." Users pay programmers, which makes the users' impressions of speed more important.

Overall, the compilation speed was excellent (less than five seconds) for all compilers tested. QB 4.0 and MB 6.0 are much slower at generating an EXE file. Each invokes BC.EXE and LINK.EXE from within the QB environment. TB is the fastest at generating an EXE file. MB 6.0 and QB 4.0 can "pseudo-compile" a program in memory faster than any other compiler and have the best facilities for tracing and debugging.

I evaluated each of the compilers for speed on 27 criteria (some compilers underwent 11 more tests). Times for each tested operation were derived by subtracting times for an appropriate empty loop. Times for a simple assignment were also subtracted for operations that included one. The times remaining are then (as nearly as possible) those for the tested operation alone.

The results are shown in Table 1, this page. Generally, TB is the fastest at integer assignments and comparisons and at printing to the screen. QB 3.0 is fastest at nearly everything else, though MB 6.0 is just as fast (or slower by only trifling amounts) on more than half the tests. QB 4.0 and TB are slow at most floating-point operations, taking more than 20 times as long as QB 3.0 for most assignments and comparisons. TB is much slower than QB 4.0 or MB 6.0 at long-integer operations, taking as much as 160 times as long as MB 6.0 to perform a long integer add.

These tests are incomplete. A definitive set would require a far larger article. TB is far better at string manipulation than the tests show. With string arrays filling most available memory, TB actually gets faster at string operations, while the Microsoft product get exponentially slower. By filling string space appropriately, you can make TB faster at string operations than any QB by any factor desired.

Size

File sizes are shown in Table 2, this page. Generally, TB and QB 3.0 produced EXE files of comparable size. QB 4.0 produced significantly smaller EXE files than either, and MB 6.0 produced the smallest.

None of these compilers seems to do much optimization, and all include too much "dead" code in the final EXE. As an educated guess, BENCHOLD.EXE should have been smaller than 10K, and BENCHNEW.EXE less than 4K. All these compilers included at least 25K of unneeded code.

Reliability

Each of the compilers was used for some months or years. Generally, all are reasonably "clean." Basic 4.0 is the least reliable, but that should be expected since it is the newest. MB-6.0 is an updated version of QB 4.0, and has fewer bugs.

In my experience, the Microsoft products make too many assumptions about the underlying hardware and DOS. MB 6.0 is no different. A program I compiled for the OS/2 environment failed with an "out of memory" error on a PS/2 Model 50 with 2 Mbyte of memory; ran but had difficulties with the Norton Guide to the OS/2 API on a Compaq DeskPro 386; and ran fine on an AT clone with a standard keyboard, but failed to recognize an enhanced keyboard on the same machine.

QB 4.0 will not correctly deallocate memory if run under DOS 2.1. Programs that use dynamic arrays will not chain or run other programs. If programs compiled with QB 4.0 are run on machines with older BIOS chips, the cursor is mangled on exit. The QB 4.0 and MB 6.0 editors must be patched to work correctly on keyboards with a separate cursor control pad, and programs compiled with either QB 4.0 or MB 6.0 may have trouble with nonstandard keyboards.

The QB 4.0 editor environment has an aggravating tendency to lock up the computer or to crash. Usually, this happens when a program is large or when many breakpoints or watchpoints have been set. Most of the problems go away under DOS 3.x, but that is of little comfort if you have only DOS 2.1.

Some of these problems derive from inadequate testing, which Microsoft has promised to correct. Some come from undoubted bugs in various versions of DOS or with older versions of the BIOS, Others are more ill-considered. I see no reason for a compiler to go directly to the hardware or generate code that does, I'm willing to write routines that go directly to the screen and to take responsibility for any problems, but I'm unhappy about compilers that create hardware-dependent problems for me.

Every one of these compilers has trouble with some keyboards. The TB editor will not recognize CTRLBREAK on one of mine. Every compiler can be forced to generate programs that work with all keyboards (by writing an assembler routine that uses normal DOS services to capture keystrokes).

If you intend to use any of these compilers, I recommend that you join a user's group or a SIG on one of the computer networks (CompuServe, GEnie, and BIX are especially good). No product is perfect, and the more complex products have more bugs. Each of these products is complex. Considering that, they are remarkably bug-free.

Interfacing to Other Languages

Microsoft's QB 4.0 and MB 6.0 win this contest. Microsoft provides explicit support for linking either product with C, Pascal, Fortran, and assembler. The manuals contain detailed examples of working programs with such interfaces.

Borland has a reasonable treatment of assembler interface, but no real provision for other languages. The lack of a link step makes such an interface difficult, at best.

Code Portability

All versions generally are portable to about the same extent: good within the MS-DOS world (but remember my earlier comments about keyboards) and poor elsewhere. All show great similarities to Macintosh Basic, which is no surprise since Microsoft wrote it. I've seen reports that another company has released a version of Basic (compatible with QBI for Unix systems. Microsoft Basic for the Xenix system has not changed much since the days of CP/M BASCOM 5.3.

If Microsoft were to release a QB product for Unix, that would presumably give many people a reason to choose Unix rather than OS/2. I suspect, for that reason, that no QB will be released for Unix or Xenix for at least several more years.

Programming Environment

This part is tricky. What I like, you may not. I will try to separate (or at least note) my personal preferences.

Microsoft has shown a steadily increasing trend to a windowed environment with dialog boxes and an equally obvious preference for a mouse. I find these trends distressing and counter-productive. I know three other programmers who use QB 4.0 on a frequent basis. I am the only one who even uses the editor, and I don't like it. Considering the powerful debugging commands in QB 4.0, that's a strong indictment of the editor.

Few programmers seem to like dialog boxes. That may be because we are programmers and not first-time computer users. Further, all programmers have a style. Some like to capitalize everything, and some prefer all lowercase. Some like to separate variables from operators, others do not. The Microsoft QB 4.0 editor will not permit many variations. It will capitalize all keywords, separate all variables and operators, and refuse to permit a line-continuation underline character. Of all the bad things the Microsoft editor does, mandatory formatting is the worst. I want source code to remain as I typed it!

TB's editor is nicer than any of Microsoft's. It is more like WordStar, which means it demands less effort to learn and use. Further, it supports the block read and write commands, and none of Microsoft's editors do (a significant problem). The TB environment is also window-oriented, but less obtrusively so while editing. Further, the commands and prompts appear in predictable locations (not true for the QB 4.0 and MB 6.0 dialog boxes).

The results obtained in the editor environment should match those obtained from the EXE file. QB 4.0 has some differences in math, which have caused problems with my double-precision routines.

If you need debugging help, any of the Microsoft products are superior to TB. While TB will allow you to trace execution of your program and step by line number or label, the Microsoft products allow you to single-step by statement, track values of variables or expressions, and compute expressions (even open or close files). To set a breakpoint with TB or QB 3.0, you must insert a command in your source code. QB 4.0 and MB 6.0 allow breakpoints to be set with a single keypress and no alteration of the source.

QB 4.0 and MB 6.0 are better at detecting errors while entering source code. Missing parentheses and most illegal commands (or statements) are detected instantly and the error location is shown. None of these compilers is good at pinpointing errors such as a missing END IF, WEND, or NEXT (and it is difficult to see how they could be). The messages and locations given for such errors are generally misleading or just plain wrong.

Run-time errors are easier to find and fix with TB, since the editor can point to the location given by the mn-time support package. For the QB products, finding the location of a run-time error generally means recompiling and generating a MAP file, then manually searching the (large) resulting text file.

Summary and Conclusions

Overall, these versions of Basic are greatly improved over those of just a few years ago. The improvements have been especially strong in documentation, modularity, and overall power. Despite that, these Basics lack support for some useful data types: unsigned integers, pointers, and BCD numbers. Matrix operations, a built-in sort command, and ISAM support are still lacking. Code optimization is weak or nonexistent, but overall performance is acceptable because of the great number of built-in functions.

Any of these newer Basics are acceptable products for large projects. For programs that use mainly integer math, must manipulate large string arrays, or are limited by screen display speed, TB is a good choice. QB 4.0 has better long integer and floating-point speed than TB, with very nearly the same screen speed. QB 3.0 is fastest but least accurate for math, slowest at screen display, and has no support for long integers. MB 6.0 is second-fastest at overall math speed, with greatest math accuracy and almost the same display speed as TB, but it is the most expensive product.

Programs that need single arrays of larger than 64K, user-definable data types, OS/2 support, or static strings must use either QB 4.0 or MB 6.0. Programs that must run under DOS 2.1 should be compiled with QB 3.0 or TB. Recursion is not supported by QB 3.0.

Because of these requirements and limitations, the professional programmer should have at least TB and one of the QB products. I'd recommend MB 6.0 and TB if you want only two compilers, but QB 3.0 and 4.0 are another attractive pair. For general utility, a good Basic compiler is hard to beat---and any of these products is better than merely good!

Table 1: Benchmark Operations Per Million


                   Time, in Seconds, per 1,000,000 Operations

                                 QB 3.0     QB 4.0  BASIC 6.0           TB
Empty integer loop                 1,21       1.21       1.21         2.09
Integer assignment                 1.15       1.13       1.13         1.04
Integer add                         .30        .49        .50          .44
Integer subtract                   3.33        .46        .49          .44
Integer multiply                   1.92       2.45       2.27         2.75
Integer divide                     3.36       3.43       3.44         4.01
String assignment                 77.09      78.50      79.04       176.91
String MID$ operation             20.78      24.61      25.20       146.18
String concatenation            1657.81     954.84    1661.15      1848.25
Single-precision assignment        5.71     201.23      20.40       269.46
Single-precision add              23,97     128.83      24.41       206,25
Single-precision subtract         24.80     129.36      25.87       206.52
Single-precision multiply         34,30     -16.19      35.50       176.62
Single-precision divide           35.98       7.48      47.68       207.92
Error, 100K single-p mult/div -1.06E-05  -1.19E-07  -1.19E-09    -1.19E-07
Single precision expontial       766.80    3061.84    1296.36      2805.25
Double-precision assignment        6.26     211.41      23.06       279.07
Double-precision add              45.75     143.28      49.99       152.75
Double-precision subtract         47.19     143.87      51.52       151.10
Double-precision multiply         83.13     199.49      91.66       185.67
Double-precision divide           84.22     226.45     121.82       212.81
Error, 100K double-p mult/div  4.82E-13  -2.22E-16   2.22E-16     6.66E-16
Double-precision exponential    2491.80    6377.23     304633      2851.74
Integer comparison                 2.58       3.20       2.56         2.31
Single-precision comparison       19.72     402.00      42.68        444A8
Double-precision comparison       20.37     412.89      46.30       452.70
Conditional int assignment         4.51       4.77       4.67         2.88
Conditional assignment*            4.72       4.86       4.34         3.37
Print 1K 70-byte strings**        26.47      10.00      10.05         9.88
Empty long integer loop                      14.45      14.28      1071.42
Long Integer assignment                       1.62       1.71       274.60
Long integer add                             15.47      15.26      1236.16
Long Integer subtract                        15.41      15.21      1235.66
Long Integer multiply                        26.11      25.82      1252.45
Long integer divide                          33.76      33.66      1521.17
Fixed string assignment                      52.38      51.92
Fixed string MID$ operation                  23.44      24.12
Fixed string concatenation                   24.61      24.78
Long integer comparison                      10.81      10.98       449.70
Print 1K 70-byte static strings**            10.16      10.10

*Reversed Order. Unreversed should be faster.

**To the screen. Monochrome display. MDA card.

Note: the negative and small times for single-precision multiply and divide for 06 4.0 don't mean th operations took place in negative time, but that they took less time to execute than a simple assignment. Perhaps there are unnecessary checking or conversion operations going on during the simple assignment. Considering the big slow-down from 063.0, that seems likely.


Table 2 File Sizes

                       File Sizes

File               QB 3.0     QB 4.0  BASIC 6.0  Turbo BASIC
BENCHOLD.BA5        7,400      7,527      7,417        7,460
BENCHOLD.OBJ       12,522     12,092     14,178
BENCHOLD.EXE       45,584     37.569     35.407       44,691
BENCHNEW.BAS                   2,887      2,887        1,788
BENCHNEW.OBJ                   6,393      7,035
BENCHNEW.EXE                  34,073     29,029       38,784

Note: Benchold.bas was slightly modified for QB 4.0 and TB because of their very slow performance on a number of the benchmark tests. Benchnew.bas was modified for TB, since TB does not have fixed-length strings. All programs were saved as text tiles and compiled with all trapping and checking options turned off (where that was possible).


Examining Room: Getting Down to Basics by Bruce Tonkin

[LISTING ONE]

<a name="0159_0014">

DEFLNG A-Z
DIM t!(28)
OPEN "bas6new.tim" FOR OUTPUT AS #1
DIM x1 AS STRING * 1
DIM x26 AS STRING * 26
DIM x70 AS STRING * 70
DIM x10000 AS STRING * 10000

'time for a raw long integer loop, executed 1,000,000 times.
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  NEXT j
NEXT i
t!(0) = TIMER - t!

'time for 1,000,000 long integer assignments.
y = 5&: z = -5&
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = y
  x = z
  NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 long integer adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x + y
  NEXT j
NEXT i
t!(2) = TIMER - t! - t!(1)

'time for 1,000,000 long integer subtracts
x = 0
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x - y
  NEXT j
NEXT i
t!(3) = TIMER - t! - t!(1)

'time for 1,000,000 long integer multiplies
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = i * j
  NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)

'time for 1,000,000 long integer divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = i \ j
  NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)

'time for 100,000 fixed string assignments
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  x26 = "abcdefghijklmnopqrstuvwxyz"
  x26 = "zyxwvutrsqponmlkjihgfedcba"
  NEXT j
NEXT i
t!(6) = (TIMER - t! - t!(0) / 10) / 2

'time for 100,000 fixed string MID$ operations
k = 17
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  MID$(x26, k, 1) = "d"
  NEXT j
NEXT i
t!(7) = TIMER - t! - t!(0) / 10

'time for 10,000 fixed string "concatenations"
x$ = ""
t! = TIMER
FOR i = 1 TO 10000
  MID$(x10000, i, 1) = "a"
NEXT i
t!(8) = TIMER - t! - t!(0) / 100

'following are logical comparisons and operators

'time for 1,000,000 long integer comparisons
x = 5&: y = -5&
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF i < y THEN x = 1
  NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)

'screen output: print 1,000 70-byte fixed strings
x70 = STRING$(70, 66)
t! = TIMER
FOR i = 1 TO 1000
  PRINT x70
NEXT i
t!(28) = TIMER - t! - t!(0) / 1000


'print results of benchmark
PRINT #1, "Raw long integer loop, 1,000,000 iterations:"; TAB(45); t!(0)
PRINT #1, "1,000,000 long integer assignments:"; TAB(45); t!(1)
PRINT #1, "1,000,000 long integer additions:"; TAB(45); t!(2)
PRINT #1, "1,000,000 long integer subtractions:"; TAB(45); t!(3)
PRINT #1, "1,000,000 long integer multiplications:"; TAB(45); t!(4)
PRINT #1, "1,000,000 long integer divisions:"; TAB(45); t!(5)
PRINT #1, "100,000 fixed string assignments:"; TAB(45); t!(6)
PRINT #1, "100,000 fixed string MID$ operations:"; TAB(45); t!(7)
PRINT #1, "10,000 fixed string concatenations:"; TAB(45); t!(8)
PRINT #1, "1,000,000 long integer comparisons:"; TAB(45); t!(23)
PRINT #1, "Print 1,000 70-byte strings to the screen:"; TAB(45); t!(28)
END



<a name="0159_0015"><a name="0159_0015">
<a name="0159_0016">
[LISTING TWO]
<a name="0159_0016">

[FILENAME:  BENCHNEW.TB]

DEFLNG A-Z
DIM t!(28)
OPEN "c:tbnew.tim" FOR OUTPUT AS #1

'time for a raw long integer loop, executed 1,000,000 times.
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  NEXT j
NEXT i
t!(0) = TIMER - t!

'time for 1,000,000 long integer assignments.
y = 5: z = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  x = y
  x = z
  NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 long integer adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  x = x + y
  NEXT j
NEXT i
t!(2) = TIMER - t! - t!(1)

'time for 1,000,000 long integer subtracts
x = 0
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  x = x - y
  NEXT j
NEXT i
t!(3) = TIMER - t! - t!(1)

'time for 1,000,000 long integer multiplies
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  x = i * j
  NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)

'time for 1,000,000 long integer divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
  x = i \ j
  NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)

'following are logical comparisons and operators

'time for 1,000,000 long integer comparisons
x = 5: y = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
    IF i < y THEN x = 1
  NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)

'print results of benchmark
PRINT #1, "Raw long integer loop, 1,000,000 iterations:"; TAB(45); t!(0)*100
PRINT #1, "1,000,000 long integer assignments:"; TAB(45); t!(1)*100
PRINT #1, "1,000,000 long integer additions:"; TAB(45); t!(2)*100
PRINT #1, "1,000,000 long integer subtractions:"; TAB(45); t!(3)*100
PRINT #1, "1,000,000 long integer multiplications:"; TAB(45); t!(4)*100
PRINT #1, "1,000,000 long integer divisions:"; TAB(45); t!(5)*100
PRINT #1, "1,000,000 long integer comparisons:"; TAB(45); t!(23)*100
END




<a name="0159_0017"><a name="0159_0017">
<a name="0159_0018">
[LISTING THREE]
<a name="0159_0018">

[FILENAME: BENCHOLD.BAS]

DEFINT A-Z
DIM t!(28)
OPEN "bas6.tim" FOR OUTPUT AS #1
'time for a raw integer loop, executed 1,000,000 times.
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  NEXT j
NEXT i
t!(0) = TIMER - t!

'time for a integer assignment loop, executed 1,000,000 times.
y = 5: z = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = y
  x = z
  NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 integer adds
y = 5: z = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x + y
  x = x + z
  NEXT j
NEXT i
t!(2) = (TIMER - t! - t!(0)) / 2 - t!(1)

'time for 1,000,000 integer subtracts
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x - y
  x = x - z
  NEXT j
NEXT i
t!(3) = (TIMER - t! - t!(0)) / 2 - t!(1)

'time for 1,000,000 integer multiplies
k = 7
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = k * j
  NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)

'time for 1,000,000 integer divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = i \ j
  NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)

'time for 100,000 string assignments
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  x$ = "abcdefghijklmnopqrstuvwxyz"
  x$ = "zyxwvutsrqponmlkjihgfedcba"
  NEXT j
NEXT i
t!(6) = (TIMER - t! - t!(0) / 10) / 2

'time for 100,000 string MID$ operations
x$ = "abcdefghijklmnopqrstuvwxyz"
k = 17
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  MID$(x$, k, 1) = "d"
  NEXT j
NEXT i
t!(7) = TIMER - t! - t!(0) / 10

'time for 10,000 string concatenations
x$ = ""
t! = TIMER
FOR i = 1 TO 10000
   x$ = x$ + "a"
NEXT i
t!(8) = TIMER - t! - t!(6) / 10 - t!(0) / 100

'time for a single-precision assignment loop, executed 1,000,000 times.
y! = 5!: z! = -5!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x! = y!
  x! = z!
  NEXT j
NEXT i
t!(9) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 single-precision adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    x! = x! + y!
    x! = x! + z!
  NEXT j
NEXT i
t!(10) = (TIMER - t! - t!(0)) / 2 - t!(9)

'time for 1,000,000 single-precision subtracts
x! = 0!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    x! = x! - y!
    x! = x! - z!
  NEXT j
NEXT i
t!(11) = (TIMER - t! - t!(0)) / 2 - t!(9)

'time for 100,000 single-precision multiplies
x! = 1!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! * 1.00001
  NEXT j
NEXT i
t!(12) = TIMER - t! - t!(0) / 10 - t!(9) / 10

'time for 100,000 single-precision divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! / 1.00001
  NEXT j
NEXT i
t!(13) = TIMER - t! - t!(0) / 10 - t!(9) / 10

'error in single-precision multiply/divide
t!(14) = x! - 1!

'time for 10,000 single-precision exponentiations
x! = 100!
t! = TIMER
FOR i = 1 TO 10000
  x! = x! ^ .999999
NEXT i
t!(15) = TIMER - t! - t!(0) / 100 - t!(9) / 100

'time for a double-precision assignment loop, executed 1,000,000 times.
y# = 5.5#: z# = -5.5#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x# = y#
  x# = z#
  NEXT j
NEXT i
t!(16) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 double-precision adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    x# = x# + y#
  NEXT j
NEXT i
t!(17) = TIMER - t! - t!(16) - t!(0)

'time for 1,000,000 double-precision subtracts
x# = 0#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    x# = x# - y#
  NEXT j
NEXT i
t!(18) = TIMER - t! - t!(16) - t!(0)

'time for 100,000 double-precision multiplies
x# = 1#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x# = x# * 1.00001#
  NEXT j
NEXT i
t!(19) = (TIMER - t! - t!(0) / 10) - t!(16) / 10

'time for 100,000 double-precision divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x# = x# / 1.00001#
  NEXT j
NEXT i
t!(20) = (TIMER - t! - t!(0) / 10) - t!(16) / 10

'error in double-precision multiply/divide
t!(21) = x# - 1#

'time for 10,000 double-precision exponentiations
x# = 100#
t! = TIMER
FOR i = 1 TO 10000
  x# = x# ^ .999999#
NEXT i
t!(22) = (TIMER - t! - t!(0) / 100) - t!(16) / 100


'following are logical comparisons and operators

'time for 1,000,000 integer comparisons
x = 0
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF i < x THEN x = 1
  NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)

'time for 1,000,000 single-precision comparisons
x! = 5!: y! = 3.333
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF x! < y! THEN x = 1
  NEXT j
NEXT i
t!(24) = TIMER - t! - t!(0)

'time for 1,000,000 double-precision comparisons
x# = 5#: y# = 3.333#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF x# < y# THEN x = 1
  NEXT j
NEXT i
t!(25) = TIMER - t! - t!(0)

'is there short-circuit expression evaluation?
'integer loop, 1,000,000 times
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF i < 0 AND j < 10 THEN x = 1
  NEXT j
NEXT i
t!(26) = TIMER - t! - t!(0)
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
    IF j < 10 AND i < 0 THEN x = 1
  NEXT j
NEXT i
t!(27) = TIMER - t! - t!(0)
'Note: if the two times are appreciably different, some optimization has been
'done.  The first time should be shorter than the second.

'screen output: print 1,000 70-byte strings
x$ = STRING$(70, 66)
t! = TIMER
FOR i = 1 TO 1000
  PRINT x$
NEXT i
t!(28) = TIMER - t! - t!(0) / 1000


'print results of benchmark
PRINT #1, "Empty integer loop, 1,000,000 iterations:"; TAB(46); t!(0)
PRINT #1, "1,000,000 integer assignments:"; TAB(46); t!(1)
PRINT #1, "1,000,000 integer additions:"; TAB(46); t!(2)
PRINT #1, "1,000,000 integer subtractions:"; TAB(46); t!(3)
PRINT #1, "1,000,000 integer multiplications:"; TAB(46); t!(4)
PRINT #1, "1,000,000 integer divisions:"; TAB(46); t!(5)
PRINT #1, "100,000 string assignments:"; TAB(46); t!(6)
PRINT #1, "100,000 string MID$ operations:"; TAB(46); t!(7)
PRINT #1, "10,000 string concatenations:"; TAB(46); t!(8)
PRINT #1, "1,000,000 single-precision assignments:"; TAB(46); t!(9)
PRINT #1, "1,000,000 single-precision additions:"; TAB(46); t!(10)
PRINT #1, "1,000,000 single-precision subtractions:"; TAB(46); t!(11)
PRINT #1, "100,000 single-precision multiplications:"; TAB(46); t!(12)
PRINT #1, "100,000 single-precision divisions:"; TAB(46); t!(13)
PRINT #1, "Error in 100,000 single-precision mult/div:"; TAB(46); t!(14)
PRINT #1, "10,000 single-precision exponentiations:"; TAB(46); t!(15)
PRINT #1, "1,000,000 double-precision assignments:"; TAB(46); t!(16)
PRINT #1, "1,000,000 double-precision additions:"; TAB(46); t!(17)
PRINT #1, "1,000,000 double-precision subtractions:"; TAB(46); t!(18)
PRINT #1, "100,000 double-precision multiplications:"; TAB(46); t!(19)
PRINT #1, "100,000 double-precision divisions:"; TAB(46); t!(20)
PRINT #1, "Error in 100,000 double-precision mult/div:"; TAB(46); t!(21)
PRINT #1, "10,000 double-precision exponentiations:"; TAB(46); t!(22)
PRINT #1, "1,000,000 integer comparisons:"; TAB(46); t!(23)
PRINT #1, "1,000,000 single-precision comparisons:"; TAB(46); t!(24)
PRINT #1, "1,000,000 double-precision comparisons:"; TAB(46); t!(25)
PRINT #1, "1,000,000 conditional integer assignments:"; TAB(46); t!(26)
PRINT #1, "1,000,000 conditional assignments (reversed):"; TAB(46); t!(27)
PRINT #1, "Print 1,000 70-byte strings to the screen:"; TAB(46); t!(28)
END




<a name="0159_0019"><a name="0159_0019">
<a name="0159_001a">
[LISTING FOUR]
<a name="0159_001a">

[FILENAME: BENCHOLD.TB]

DEFINT A-Z
DIM t!(28)
OPEN "bas6.tim" FOR OUTPUT AS #1
'time for a raw integer loop, executed 1,000,000 times.
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  NEXT j
NEXT i
t!(0) = TIMER - t!

'time for a integer assignment loop, executed 1,000,000 times.
y = 5: z = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = y
  x = z
  NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2

'time for 1,000,000 integer adds
y = 5: z = -5
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x + y
  x = x + z
  NEXT j
NEXT i
t!(2) = (TIMER - t! - t!(0)) / 2 - t!(1)

'time for 1,000,000 integer subtracts
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = x - y
  x = x - z
  NEXT j
NEXT i
t!(3) = (TIMER - t! - t!(0)) / 2 - t!(1)


'time for 1,000,000 integer multiplies
k = 7
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = k * j
  NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)

'time for 1,000,000 integer divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 1000
  x = i \ j
  NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)

'time for 100,000 string assignments
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  x$ = "abcdefghijklmnopqrstuvwxyz"
  x$ = "zyxwvutsrqponmlkjihgfedcba"
  NEXT j
NEXT i
t!(6) = (TIMER - t! - t!(0) / 10) / 2

'time for 100,000 string MID$ operations
x$ = "abcdefghijklmnopqrstuvwxyz"
k = 17
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  MID$(x$, k, 1) = "d"
  NEXT j
NEXT i
t!(7) = TIMER - t! - t!(0) / 10

'time for 10,000 string concatenations
x$ = ""
t! = TIMER
FOR i = 1 TO 10000
   x$ = x$ + "a"
NEXT i
t!(8) = TIMER - t! - t!(6) - t!(0) / 100

'time for a single-precision assignment loop, executed 1,000,000 times.
y! = 5!: z! = -5!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  x! = y!
  x! = z!
  NEXT j
NEXT i
t!(9) = (TIMER - t! - t!(0)/10) / 2

'time for 1,000,000 single-precision adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! + y!
    x! = x! + z!
  NEXT j
NEXT i
t!(10) = (TIMER - t! - t!(0)/10) / 2 - t!(9)

'time for 1,000,000 single-precision subtracts
x! = 0!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! - y!
    x! = x! - z!
  NEXT j
NEXT i
t!(11) = (TIMER - t! - t!(0)/10) / 2 - t!(9)

'time for 100,000 single-precision multiplies
x! = 1!
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! * 1.00001
  NEXT j
NEXT i
t!(12) = (TIMER - t! - t!(0) / 10) / 2 - t!(9)

'time for 100,000 single-precision divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x! = x! / 1.00001
  NEXT j
NEXT i
t!(13) = (TIMER - t! - t!(0) / 10) / 2 - t!(9)

'error in single-precision multiply/divide
t!(14) = x! - 1!

'time for 10,000 single-precision exponentiations
x! = 100!
t! = TIMER
FOR i = 1 TO 1000
  x! = x! ^ .999999
NEXT i
t!(15) = (TIMER - t! - t!(0) / 1000) / 2 - t!(9) / 100

'time for a double-precision assignment loop, executed 1,000,000 times.
y# = 5.5#: z# = -5.5#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
  x# = y#
  x# = z#
  NEXT j
NEXT i
t!(16) = (TIMER - t! - t!(0)/10) / 2

'time for 1,000,000 double-precision adds
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x# = x# + y#
  NEXT j
NEXT i
t!(17) = TIMER - t! - t!(16) - t!(0)/10

'time for 1,000,000 double-precision subtracts
x# = 0#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    x# = x# - y#
  NEXT j
NEXT i
t!(18) = TIMER - t! - t!(16) - t!(0)/10

'time for 100,000 double-precision multiplies
x# = 1#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
    x# = x# * 1.00001#
  NEXT j
NEXT i
t!(19) = (TIMER - t! - t!(0) / 100) - t!(16) / 10

'time for 100,000 double-precision divides
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 10
    x# = x# / 1.00001#
  NEXT j
NEXT i
t!(20) = (TIMER - t! - t!(0) / 100) - t!(16) / 10

'error in double-precision multiply/divide
t!(21) = x# - 1#

'time for 10,000 double-precision exponentiations
x# = 100#
t! = TIMER
FOR i = 1 TO 1000
  x# = x# ^ .999999#
NEXT i
t!(22) = (TIMER - t! - t!(0) / 1000) - t!(16) / 100


'following are logical comparisons and operators

'time for 1,000,000 integer comparisons
x = 0
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    IF i < x THEN x = 1
  NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)/10

'time for 1,000,000 single-precision comparisons
x! = 5!: y! = 3.333
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    IF x! < y! THEN x = 1
  NEXT j
NEXT i
t!(24) = TIMER - t! - t!(0)/10

'time for 1,000,000 double-precision comparisons
x# = 5#: y# = 3.333#
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    IF x# < y# THEN x = 1
  NEXT j
NEXT i
t!(25) = TIMER - t! - t!(0)/10

'is there short-circuit expression evaluation?
'integer loop, 1,000,000 times
t! = TIMER
FOR i = 1 TO 1000
  FOR j = 1 TO 100
    IF i < 0 AND j < 10 THEN x = 1
  NEXT j
NEXT i
t!(26) = TIMER - t! - t!(0)/10
t! = TIMER

FOR i = 1 TO 1000
  FOR j = 1 TO 100
    IF j < 10 AND i < 0 THEN x = 1
  NEXT j
NEXT i
t!(27) = TIMER - t! - t!(0)/10
'Note: if the two times are appreciably different, some optimization has been
'done.  The first time should be shorter than the second.

'screen output: print 1,000 70-byte strings
x$ = STRING$(70, 66)
t! = TIMER
FOR i = 1 TO 1000
  PRINT x$
NEXT i
t!(28) = TIMER - t! - t!(0) / 1000


'print results of benchmark
PRINT #1, "Empty integer loop, 1,000,000 iterations:"; TAB(46); t!(0)
PRINT #1, "1,000,000 integer assignments:"; TAB(46); t!(1)
PRINT #1, "1,000,000 integer additions:"; TAB(46); t!(2)
PRINT #1, "1,000,000 integer subtractions:"; TAB(46); t!(3)
PRINT #1, "1,000,000 integer multiplications:"; TAB(46); t!(4)
PRINT #1, "1,000,000 integer divisions:"; TAB(46); t!(5)
PRINT #1, "100,000 string assignments:"; TAB(46); t!(6)
PRINT #1, "100,000 string MID$ operations:"; TAB(46); t!(7)
PRINT #1, "10,000 string concatenations:"; TAB(46); t!(8)
PRINT #1, "1,000,000 single-precision assignments:"; TAB(46); t!(9)*10
PRINT #1, "1,000,000 single-precision additions:"; TAB(46); t!(10)*10
PRINT #1, "1,000,000 single-precision subtractions:"; TAB(46); t!(11)*10
PRINT #1, "100,000 single-precision multiplications:"; TAB(46); t!(12)
PRINT #1, "100,000 single-precision divisions:"; TAB(46); t!(13)
PRINT #1, "Error in 100,000 single-precision mult/div:"; TAB(46); t!(14)
PRINT #1, "10,000 single-precision exponentiations:"; TAB(46); t!(15)*10
PRINT #1, "1,000,000 double-precision assignments:"; TAB(46); t!(16)*10
PRINT #1, "1,000,000 double-precision additions:"; TAB(46); t!(17)*10
PRINT #1, "1,000,000 double-precision subtractions:"; TAB(46); t!(18)*10
PRINT #1, "100,000 double-precision multiplications:"; TAB(46); t!(19)*10
PRINT #1, "100,000 double-precision divisions:"; TAB(46); t!(20)*10
PRINT #1, "Error in 100,000 double-precision mult/div:"; TAB(46); t!(21)*10
PRINT #1, "10,000 double-precision exponentiations:"; TAB(46); t!(22)*10
PRINT #1, "1,000,000 integer comparisons:"; TAB(46); t!(23)*10
PRINT #1, "1,000,000 single-precision comparisons:"; TAB(46); t!(24)*10
PRINT #1, "1,000,000 double-precision comparisons:"; TAB(46); t!(25)*10
PRINT #1, "1,000,000 conditional integer assignments:"; TAB(46); t!(26)*10
PRINT #1, "1,000,000 conditional assignments (reversed):"; TAB(46); t!(27)*10
PRINT #1, "Print 1,000 70-byte strings to the screen:"; TAB(46); t!(28)
END










Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.