At times, the migrated .NET application performs noticeably slower than the original application. The reasons for this loss of performance can be many, and this article explains the most common causes and the available workarounds.
The most common cause for a perceived slow .NET application is the fact that a .NET assembly has to be JIT-compiled before it can execute. The JIT compilation step can take quite a while, especially with Windows Forms application, because all the libraries referenced by the main project have to be JIT compiled.
You can avoid JIT compilation by pre-compiling the .NET project with the NGEN utility, as explain in this KB article.
String concatenation – that is, the “&” string operator – can be quite slow under .NET, especially when the concatenation is repeated many times. For this reason, VB Migration Partner conveniently emits a warning when a concatenation is found inside a loop. It is quite common that such concatenation in converted code are 10x or 100x slower than in the original VB6 code.
You can make these concatenations as fast or even faster than under VB6 by means of the .NET StringBuilder object. Even better, you can use the special StringBuilder6 type exposed by VB Migration Partner’s support library, which can be applied with minimal or no intervention on the migrated code.
You know that accessing a method in late binding mode is quite slow under VB6, but you probably don’t know that it is even slower under VB.NET and under C# (when dynamic variables are used). For this reason, you should try to stay away from late binding in migrated code, for example by using the InferType pragma.
If late binding can’t be avoided, you might optimize your code by using more specific techniques based on .NET reflection. For example, using the Type.GetMethod method once to obtain a MethodInfo object, and then calling the MethodInfo.Invoke method multiple times is always faster than using pure late binding.
Error handling in migrated code works exactly like in the original VB6 code, except that it usually adds a big performance penalty. This penalty is especially evident when the application runs in Debug mode, but it is sometime unacceptable even when running in Release mode.
After the migration you should optimize your code in order to reduce the number of On Error statements, especially On Error Resume Next. In general, .NET offers many ways to avoid that an exception is throw. For example, you can and should check whether a file exists before attempting to open it; you can check whether a collection item exists before trying to access it, and so forth.
The VB6Variant type
VB Migration Partner offers the special VB6Variant type, which almost perfectly emulates the behavior of the VB6 Variant data type. However, the emulation has a cost, and applications that massively use the VB6Variant might run noticeably slower in come cases.
The obvious remedy is trying not to use the VB6Variant type or – more realistically – trying to limit its use to small and well defined portions of the project. (Notice that the VB6Variant type isn’t supported when migrating to C#.)
If your app makes intensive use of graphic methods, you might find it convenient to replace VB6-like graphic methods with native GDI+ .NET methods. Or you can make your charts appear instantaneously by using double buffering.
Accessing a COM object – for example, an ADODB.Recordset – from a .NET client app is a bit slower than accessing the object from a VB6 project, because in the former case your call goes through the COM Interop layer.
You can reduce this overhead and improve performances by reducing the number of COM calls your code does. For example, use the Recordset.GetRows method to retrieve multiple rows in one operation, if possible. Even better, try to get rid of all your COM DLLs by replacing them with their .NET counterparts.
The ADOLibrary that comes with VB Migration Partner offers a “it just works” approach to migrating ADODB code to ADO.NET. However, perfectly emulating ADODB with ADO.NET objects doesn’t come for free, and in some cases it is required that you manually fix the migrated code in oder to make the best use of ADO.NET.
First and foremost, you should avoid using server-side keyset and dynamic cursors (which ADOLibrary currently supports only for Microsoft SQL Server) and use forwardonly-readonly or client-side cursors if possible (which ADOLibrary supports for all database kinds). This fix improves performance and scalability alike.
Second, you should try to use the native ADO.NET objects that the ADOLibrary exposes. For example, after opening a client-side ADORecordset, you can and should use the DataTable object that this object exposes to read and manipulate individual records and fields, and use the exposed DataAdapter object to apply changes to the underlying database.
Finally, you can improve performance by compiling and running your application as a native 64-bit application. To do so you must remove all dependencies from ADODB and other COM libraries (see previous two paragraphs).