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.
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.
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.
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.
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!
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,
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
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.
Nearly all programs contain tests for non-zero or greater-than-zero values,
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
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$
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.
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$.
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:
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.
Forms and controls
Functions, variables, constants
© 1/2010 Shamrock Software GmbH