These days I’m working to improve the startup time of an application my company develop, using some profiling tool you can easily isolate that functions that are eating up more time during the application startup.

Aside the database connections, I’ve saw that our custom protection and registration system was consuming more or less 3 seconds (it varies each run in the range from 2.5 to 3.5 seconds) to generate a unique hash code for the machine on which the program is installed.

The code internally uses some WMI queries to pickup system and hardware information, the first run was something like this:

WMI_BeforeQueryOptimization

One of the functions that is used to generate part of the hash requires 2.6 seconds to run...which is unacceptable to gather some hardware information.

All those functions basically use some WMI queries to get the data we need the implementation was something like:

   1: Friend Shared Function GetProcessorInfo() As String
   2:       Using query1 As New ManagementObjectSearcher("SELECT * FROM Win32_Processor")
   3:           Using queryCollection1 As ManagementObjectCollection = query1.Get()
   4:               Dim sb As New StringBuilder
   5:               For Each mo As ManagementObject In queryCollection1
   6:                   sb.Append(Convert.ToString(mo("processorid")))
   7:                   sb.Append(Convert.ToString(mo("family")))
   8:                   sb.Append(Convert.ToString(mo("architecture")))
   9:                   sb.Append(Convert.ToString(mo("revision"))) 
  10:                   sb.Append(Convert.ToString(mo("level")))
  11:                   sb.Append(Convert.ToString(mo("stepping")))
  12:                   sb.Append(Convert.ToString(mo("uniqueid")))
  13:                   sb.Append(Convert.ToString(mo("maxclockspeed")))
  14:                   sb.Append(Convert.ToString(mo("manufacturer")))
  15:                   Return sb.ToString
  16:               Next
  17:           End Using
  18:       End Using
  19:   End Function

Here you can see the source of all evil: in line 2 we ask the system to get EVERY information about the Processor and when we start enumeration the ManagementObjectCollection the WMI subsystem start gathering the data and populating the ManagementObject with all the information it can get regarding the Processors...even those we are not interested into. And this slows down everything in a terrible way.

So, the first thing you have to do when dealing with WMI is to reduce to the minimum the information that are returned from each query. I modified all the queries we made to look like:

Using query1 As New ManagementObjectSearcher("SELECT processorid,family,architecture,revision,level,stepping,uniqueid,maxclockspeed,manufacturer FROM Win32_Processor")
        

Running again the profiler on the function we obtain:

WMI_AfterQueryOptimization 

As you can see the execution time was cut in half! and the GetProcessorInfo() function alone passed from 1043ms to 12ms. The only function we wasn’t able to improve specifying the fields to return was the one that deals with the Ethernet interfaces to get their MAC address.

Related Content