More speed in VBFaster Visual Basic Programs

Simple tricks for VB optimization

Microsoft Visual Basic is a popular programming language. While many applications at Shamrock were written in C and C++, some were also created in VB. Especially its string concept and string functions make life easier than in other languages and also improve stability: Crashes caused by buffer overflows and null pointer problems, well-known to most C programmers, are quite rare in VB. A few simple tricks make execution speed even faster. They were tested with VB4, VB5 and VB6. Please note that the given CPU times should be read as relative values which greatly depend on the PC speed.

Use the dollar symbol for string functions

One might expect that Mid$( ) and Mid( ) are the same and the dollar symbol representing the string type is more or less implicit for string manipulating functions. But without the $, VB will return a variant value and has to reconvert it to a string later. Adding the $ is an optimization. It will explicitly enforce a string value to be returned. Running a loop 10 million times on a test PC shows significant timing differences:

e$ = mid("ABC", 2)        5.0 s
e$ = mid$("ABC", 2)       1.9 s

So a dollar symbol should be added behind all functions returning a string to avoid the unnecessary type conversion:
Chr ChrB CurDir Dir Format Hex Input InputB LCase Left LeftB Mid MidB Oct Right RightB RTrim Space Str String Trim UCase

There are also speed differences between Asc and AscW, and Chr$ and ChrW$. Because the functions with a W (for wide) do not have to convert the VB-internal 16-bit Unicode characters to 8-bit ANSI codes or vice versa, they are a bit faster. While the difference between Chr and ChrW is marginal, AscW is nearly two times as fast as Asc. But take care: AscW may return values above 255, depending on the character set used, and this is not always easy to handle.

Assigning and testing empty strings

A loop assigning an empty string to a string variable 10 million times gives surprising results, too. While absolute times may again differ on your system, the improvement when using vbNullString is significant:

e$ = ""                  1.51 s
e$ = vbNullString        0.38 s

Note that the predefined constant vbNullString is not exactly the same as an empty string, but it can be used as an equivalent and even returns true when comparing vbNullString = "" (while vbNullString is a null pointer, "" results in a pointer to a string with zero length; however, not all API/DLL functions handle them identically, so be careful).

Checking if a string is empty 10 million times can also be done in different ways. Here are the times for doing it 10 million times on our test system:

If "ABC" = ""            0.73 s
If "ABC" = vbNullString  0.58 s
If Len("ABC") = 0        0.46 s
If LenB("ABC") = 0       0.39 s

So the speed nearly doubles if you are using LenB instead of comparing the string with an empty string. Note that you should not replace Len by LenB in general, since LenB returns the byte count in the VB double-byte strings instead of the character count. But for simply checking if the length is zero, LenB is the fastest method.

Checking character codes

To check if the first character of a string matches a specified code, AscW is more than two times faster than using a conventional string comparison:

If "A" = "A"             1.17 s
if Asc("A") = 65         1.01 s
If AscW("A") = 65        0.52 s

Visual Basic always uses Unicode strings internally, so AscW does not need to convert 16-bit Unicode values to 8-bit ANSI values like Asc. Note, however, that AscW (and also Asc) will cause a runtime error if the given string is empty, so take care. Furthermore, AscW returns Unicode values instead of ANSI codes, but this does not make a difference for ASCII codes from 0 to 127. For national symbols, the result depends on the system's default character code.

String to number conversion

When you convert a string to a numeric variable, the most common function is Val( ). But if the numeric variable is an integer or a long integer, why waste time for floating-point calculations? Converting the string to the desired variable is much faster. The following example shows the required time for converting a string "1234567" to a long integer 10 million times:

i& = Val(e$)             24.2 s
i& = CLng(e$)             6.5 s

However, unfortunately, the behaviour of CLng (32 bit) and CInt (16 bit) is a bit different compared to Val:

What's better, Visual Basic or C?
Good question, just like "what's better, apples or oranges". If a program must never crash due to unexpected string lengths or similar causes, VB is a good choice, but do not forget to handle all possible runtime errors properly. If speed is more important, assembler is best, immediately followed by C.

So Val might still be the better choice if either the speed is not critical or if it cannot be guaranteed that the string is not strictly numerical. Be careful, though - even Val is not absolutely robust: Things like Val("1@e") will generate a run-time error. So do not use either it if you do not really expect a numerical string!

Multiple conditions in one line

In contrary to many other languages, Visual Basic (just as the QuickBasic compiler and the GwBasic interpreter in the old DOS days) executes all conditions in an IF statement, whether this makes sense or not. For instance, C, Javascript or Perl never call the Function check(2 ) in this sample if check(1) returns 0=false:
if (check(1) && check(2))

But Visual Basic always checks both, which is not really optimal since it wastes time. Here is a workaround. Instead of using
if check(1) and check(2) ...
execution will be much faster if you write:
if check(1) then if check(2) ...
Of course this is not really critical for comparing simple variables, but calling functions unnecessarily increases the CPU time. It is also useful to think about which of the conditions will be more often false than the other one(s) and put it at the beginning -- but this is a good idea for all programming languages, of course.

Similarly, in most programming languages a second condition after a logical OR ("||" in C, Javascript or Perl) is not executed if the first condition is already true. Visual Basic is different again. A demonstration example:
If True Or MsgBox("Test") then j=1
This will show a message box, even though the execution of MsgBox is totally obsolete. It will save execution time if you change the program flow so that no OR-combined statements occur.

True is non-zero

Nearly all programs contain tests for non-zero or greater-than-zero values, like:
If Instr(x$, ",") > 0 Then ...
Note that Instr never returns negative values, so <>0 is just as good as >0. Knowing this, the line can be abbreviated to:
If Instr(x$, ",") Then ...
since a condition is always treated as True if the result is non-zero. However, be careful when using Not:
If Not Instr(x$, ",") Then ...
This would not work since Not negates all bits and thus only works correctly with -1 (True, all bits set) and 0 (False, all bits reset) as Boolean values! Instead,
If Instr(x$, ",") = 0 Then ...
must be used.

Imagine you are writing a function which returns true if string a is greater than string b. A beginner would write:

Private Function Comp(a$, b$) as Boolean
 If a$ > b$ Then Comp = True Else Comp = False
End Function

Now, first of all, "Else Comp = False" is obsolete since all variables and the return value is initialized to 0, False, or empty when a function is entered. But even more tricky, the following function is equivalent to the above and faster:

Private Function Comp(a$, b$) as Boolean
 Comp = a$ > b$
End Function

Declaring Long as the default variable type

The module-level code of each form and module should always contain at least these two statements at the very beginning:

Option Explicit
DefLng A-Z

Option Explicit makes sure that you will have to declare all variables explicitly which significantly enhances code quality. DefLng A-Z defines all variables with no explicit type declaration as 32-bit long integers (called dwords in C) which is the native data type in all modern CPUs. If you forget this, your variables will be variants, resulting in a much slower program execution and more memory consumption. It is also not recommended to use 16-bit integers instead - though they are shorter, execution will be slower than with native 32-bit types. In general, avoid the Variant data type which is much slower than any other.

Copying small things to large strings

If you have to add some small strings to a long string many times, this can get extremely slow. The reason is that Visual Basic makes a copy of the large destination string each time when something is added to it. A sample which takes about 12 seconds on a test PC:

For i = 0 to 10000
 a$ = a$ & "This " & "is a test"

Something like a$ = a$ & b$ gets extremely slow when a$ gets large. A small "cache" string can speed up things significantly. The following version runs in less than 0.1 s -- more than 100 times faster though the result is still 140014 characters long!

For i = 0 to 10000
 c$ = c$ & "This " & "is a test"
 If Len(c$) > 3000 then a$ = a$ & c$: c$ = vbNullString
If LenB(c$) Then a$ = a$ & c$ 'Add rest if any

The trick is that c$ is used for chunks of data which can be copied relatively fast. Only when the chunk in c$ gets big enough it is then added to the result in a$.

Making VB 4+5 applications work in Windows 7 and Vista

When you try to launch a Visual Basic 4.0 or 5.0 program in Windows 7 or Vista for the very first time and UAC (user access control) is enabled, which is the default, it displays a message "Error accessing the system registry" and will not work. The cause is that the VB runtime DLL attempts to write values in a registry path which is not accessible for users and normal applications. There are two ways to circumvent this, and later program starts will then work without any problem:

  1. Right-click the Visual Basic program und select "Run as administrator" when launching it for the first time. - Or:
  2. Rename the program to Setup.exe or Install.exe. In this case you will be prompted if you agree to start it. An alternative is to call the application from another program named Setup.exe using the Windows API function ShellExecute( ) or the Shell command in VB.

If the program needs to register an OCX module, this will require one of the above methods even if another Visual Basic program was running before: Normal users or programs are not allowed to register such modules. Also keep in mind that only Setup programs are allowed to copy OCX files into the System32 folder.

The problems described above do not exist with VB 6 because Microsoft has included an adapted version of the VB 6 runtime library in Windows 7 and Vista which is installed automatically to avoid compatibility problems. This also fixes problems with data execution prevention (DEP) supported by newer CPUs, though this option is disabled by default for applications. Programs written in VB 4+5 are incompatible with CPU-supported DEP and will crash immediately (or not even start) if it is enabled.

More tricks

Forms and controls

Functions, variables, constants

Other recommendations

1/2010 Shamrock Software GmbH