Columns


Questions & Answers

Are Marching Pointers Really Faster?

Kenneth Pugh


Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++ language courses for corporations. He is the author of All On C, C for COBOL Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C committee. He also does custom C/C++ programming and provides SystemArchitectonicssm services. His address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax questions for Ken to (919) 489-5239, Ken also receives email at kpugh@allen.com (Internet) and on Compuserve 70125,1142.

Q

I always enjoy your column because it is always relevant. My work is scientific computing. In your piece "Increasing Execution Speed" you contrast zeroing an array using an explicit index with a method that uses pointer incrementing. You comment that the pointer increment is "usually faster." Oh? My work chart looks like Table 1.

I coded up a comparison of a million zeros each way (Listing 1) . On my 486 with Turbo C they come out nearly the same: 1.2 seconds for the index versus 1.0 seconds for the pointer. My friend Walter Herrick tested the programs on a couple of other platforms and got nearly identical results (Listing 2) , Why so nearly equal? Microprocessors do integer multiplications in hardware, which is fast, compared with shipping bytes to memory.

There are two good reasons to avoid addressing an array by a moving pointer, using an index instead. First, the pointer method does not generalize to arrays of more than one dimension. Second, the marching pointer is not language-portable. In scientific programming, one frequently has to move algorithms between C, FORTRAN, and Pascal. Only C allows moving pointers. Other languages, especially those with built-in array index validation, do not permit pointers to move.

It is true that the K&R book has many examples of marching pointers. But that does not make marching pointers a good idea! K&R was written when multiplies were expensive and adds were cheap. We are beyond that now, and can afford a different point of view, in which clarity is regarded as being more important than minimizing source code length. For one view of how to restrict ANSI C as to promote clean coding (no side effects, one consequence per line of code, transportable constructs, etc.) see the article by Helene Ballay and Rainer Store, "A Tool for Checking C Coding Conventions," CUJ, July, 1994, p. 41. From the sidebar I judge that PJP disagrees with those authors but in my experience (moving code), I support their view.

Mike Lampton
Berkeley, CA

A

Thanks for sharing your test results with us. It's true that in many cases, with today's increased microprocessor speeds, the relative efficiency of a particular approach is unimportant. This is not necessarily the case for specialized applications, such as in embedded systems, which may use less powerful chips. The use of instruction caches and memory caches complicates the issue by making it difficult to calculate which approach will always be most efficient.

With GUI interfaces becoming much more common these days, much of the CPU time is spent in interacting with the user. The application programmer usually does not have much opportunity to make the GUI efficient (other than to insure that any repainting of the screen is minimal — i.e. the entire virtual screen is not redrawn). I estimate that about 80% of the CPU time in a typical application is spent in GUI libraries and code. The reminder is in "real" processing. If you were able to double the efficiency of the real processing, you would only cut 10% from the overall CPU time.

Memory overwrites

Q

I'm a longtime subscriber to The C Users Journal and I've written a program using Turbo C v3.0 that writes to memory that doesn't belong to it, specifically memory that MS-DOS uses. After running the program, in most cases memory is so corrupted that DOS doesn't recognize the keyboard, COMMAND.COM fails to reload, or, worse yet, DOS's information on the hard disk format has been overwritten and thereafter any write to the disk destroys data there.

I've done the usual in trying to debug the program, including using MemCheck v3.0 and the latest PC-Lint. I have not figured out how to zero in on the code that is responsible using either of these two commercial tools.

I suspect some third-party commercial graphics libraries that I am using are doing the actual writing to DOS memory, but haven't been able to pinpoint the problem.

The next step I want to try is to generate checksums for various blocks of memory that DOS uses and then throughout my program periodically recalculate the checksums in an effort to move in closer to the bad code. The problem here, however, is that I've been unable to locate any detailed maps of memory that DOS uses. No one claims to know where I can obtain them.

Do you know of any source for DOS memory maps or do you have any suggestions on how I can find the cause of my problem?

Dennis C. Fait
Butler, PA

A

Memory problems are the bane of the C programmer. Give a programmer an address and there is no telling what he or she may do with it. PC-Lint can indicate potential problems in source code for many types of pointer errors. However it cannot analyze dynamic errors. For example, it is easy (too easy some might say) to increment an address beyond its proper bounds by looping too many times. Debug output placed in the source code can pinpoint these types of errors. Since it sounds like you do not have access to the source code for the third party libraries, you will have to try a more indirect approach.

Some of your problems may be caused by errors other than memory overwrites. For example, I know that several DOS versions back you could cause disk corruption by re-opening the same file for writing without closing it. (I have not tried this recently, as my disks have grown too large for easy repair in case it remains a problem with MS-DOS.)

A good book on MS-DOS programming will typically include some sort of MS-DOS memory map, though maybe not to the level of detail you are looking for. You might check out Dos Internals, by Geoff Chappell [1]. This book dedicates a couple of chapters to MS-DOS memory management; it may shed some light on your problem. Alternatively, run the MS-DOS MEM command with the /D option. This will give you a fairly detailed snapshot of your memory configuration. Most likely you will discover that MS-DOS is not confined to one contiguous region, which can make this particular debugging approach difficult.

Fortunately, just as there are many ways to create memory overwrite errors, there are many possible ways to track down those errors. For example, you can temporarily bypass calls to the suspected routines. You ought to use a dynamic test to do this, rather than using conditional compilation. Use something like this:

   if (full_program)
      potential_offending_function();
as opposed to this:

   #ifdef FULL_PROGRAM
      potential_offending_function();
   #endif
Using a dynamic test will keep the memory layout of your executable close to that for which you are having problems.

You could also try using different versions and configurations of MS-DOS and see if the results are any different. The memory layout would be different and so you should get different results. If no differences occur, the error may be in overwritting buffers internal to your program rather than in MS-DOS.

Another approach would be to compile it in protected mode and link it with a protected mode linker. Your graphic libraries may not necessarily work in that mode, although many of them do. Memory access errors in protected mode will cause a fault, rather than crash MS-DOS.

The above is not an exhaustive list. If nothing else works, you can, of course, try another graphics library. I admit that's not a very desirable option. Hopefully its interface will be similar to the one you are currently using, so you won't have to re-write much code.

Reference

[1] Geoff Chappell. Dos Internals (Addison-Wesley, 1994), pp. 131-192.