Examining Code
Before we get to my recommended way of testing performance (writing performance tests), let’s examine ways to look at your code and see exactly what goes on under the hood. It turns out that this is a little different depending on whether you use p-code or native code.
If you’re the adventurous type who isn’t afraid of disassembled machine code, you can examine Basic p-code. The key to breaking into Visual Basic code is the DebugBreak API routine. (It’s in the Windows API type library described
in Chapter 2.) In case you’re curious, its assembly language implementation looks like this:
DebugBreak PROC
int 3
ret
DebugBreak ENDP
The INT 3 instruction signals any active debugger to break out of execution. That’s how debuggers work—by temporarily putting an INT 3 wherever they want a breakpoint.
Put a DebugBreak statement in your Basic source just before the line you want to examine. You should start with something simple but recognizable:
DebugBreak
i = &HABCD
Now run your program in the Visual Basic environment. When you hit the breakpoint, an application error box will appear, telling you that you’ve hit a breakpoint. It gives you a choice of terminating Visual Basic or debugging the application. If you’re running under Windows 95, click Debug; if you’re running under Windows NT, click Cancel. You’ll pop up in your system debugger. (Mine is Microsoft Visual C++, but yours might be different.) You’ll see the INT 3 instruction, followed by a RET instruction. Step through them. When you step past RET, you’ll be in the code that calls API functions like DebugBreak. If you keep stepping through a lot of confusing code to get to the next Basic statement, eventually you’ll find yourself in the strange world of p-code.
Let’s just say that this code doesn’t look like any C or assembler code I’ve disassembled before. If you want to know more, there are articles describing the p-code concept in MSDN and in the Visual C++ manuals. The main point is how many instructions it takes to do a simple task. In disassembled C code, the example statement would translate into something like this:
mov WORD PTR i, 0ABCDh
It is sobering to see how many instructions it takes to do the same thing in
p-code.
The story is different for compiled code. All you have to do is compile with debugging information. Choose the Create Symbolic Debug Info check box and the No Optimization radio button on the Compile tab of the Project Properties dialog box. (If you don’t turn off optimizations, the compiler might rearrange code in ways that are difficult to trace through.) After compiling, you can load the executable file into a source debugger. I use the Microsoft Developer Studio (MSDEV.EXE), but any debugger that understands Microsoft’s debug format will work. For example, load from an MS-DOS session with this command line:
start msdev timeit.exe
You’ll pop up in the assembly language decode window at some undetermined point, but it is possible to debug with source code. Use the File Open command to load the startup module (or any module you’re interested in). Put a breakpoint somewhere in the code, and press F5 or the Go button. You’ll stop at your breakpoint, and you can start tracing. You might find a few surprises. The Microsoft Developer Studio isn’t designed for debugging Visual Basic code and doesn’t have a Basic expression evaluator. You might not be able to evaluate expressions the way you expect in the Watch window. If these limits seem annoying, try debugging p-code again; you’ll appreciate the native code.