Performance techniques for converting Object to structures


If you’re
converting Object expressions to
structures, you can tweak your code for a drastic performance improvement.

 

Because class=SpellE>unboxing a Nothing
reference throws an exception, a conversion from Object to a structure involves testing for a Nothing reference before performing the class=SpellE>unbox.  For
example:

 

Structure
Foo

style='mso-spacerun:yes'>    Public a style='color:blue'>As Integer

style='mso-spacerun:yes'>    Public b class=GramE>As Integer

End
Structure

 

Module
Test

style='mso-spacerun:yes'>    Sub class=SpellE>Foo( class=SpellE>ByVal o style='color:blue'>As Object)

style='mso-spacerun:yes'>        Dim x class=GramE>As Foo

 

style='mso-spacerun:yes'>        x = o

 

style='mso-spacerun:yes'>    End style='color:blue'>Sub

End
Module

 

The
compiler actually generates this code behind the scenes:

 

Module
Test

style='mso-spacerun:yes'>    Sub class=SpellE>Foo( class=SpellE>ByVal o style='color:blue'>As Object)

style='mso-spacerun:yes'>        Dim x class=GramE>As Foo

 

style='mso-spacerun:yes'>        Dim _t style='color:blue'>As Object = o

style='mso-spacerun:yes'>        If _t class=GramE>Is Nothing
Then

style='mso-spacerun:yes'>            _t = class=GramE>System.Activator.CreateInstance( class=SpellE>GetType(Foo))

style='mso-spacerun:yes'>        End style='color:blue'>If

style='mso-spacerun:yes'>        x = class=GramE>DirectCast class=GramE>(_t, Foo)

 

style='mso-spacerun:yes'>    End style='color:blue'>Sub

End
Module

 

In the
case that o is style='mso-bidi-font-weight:normal'>Nothing, x gets a zeroed-out, or default, instance of style='mso-bidi-font-style:normal'>Foo. style='mso-spacerun:yes'>  
Getting this default instance of class=SpellE>Foo is costly
because the compiler uses System.Activator.CreateInstance to create one.

 

You can
avoid the call to System.Activator.CreateInstance
by performing the Nothing check by
hand and supplying a ready-made default instance of your structure. style='mso-spacerun:yes'>  
There are two techniques I can think of to
supply a ready-made default, either by using a local variable or, slightly
safer, a Shared ReadOnly
field declared on the structure itself.

 

Option 1:

 

Module
Test

style='mso-spacerun:yes'>    Sub class=SpellE>Foo( class=SpellE>ByVal o style='color:blue'>As Object)

style='mso-spacerun:yes'>        Dim x class=GramE>As Foo

 

style='mso-spacerun:yes'>        Dim style='color:blue'>DEFAULT As
Foo    style='color:green'>‘Don’t ever modify this

style='mso-spacerun:yes'>        If o style='color:blue'>Is Nothing style='color:blue'>Then

style='mso-spacerun:yes'>            x = DEFAULT

style='mso-spacerun:yes'>         style='color:blue'>Else

style='mso-spacerun:yes'>            x = class=GramE>DirectCast class=GramE>(o, Foo)

style='mso-spacerun:yes'>        End style='color:blue'>If

 

style='mso-spacerun:yes'>    End style='color:blue'>Sub

End
Module

 

Option 2:

 

Structure
Foo

style='mso-spacerun:yes'>    Public a style='color:blue'>As Integer

style='mso-spacerun:yes'>    Public b class=GramE>As Integer

 

style='mso-spacerun:yes'>    Public style='color:blue'>Shared ReadOnly
DEFAULT style='color:blue'>As Foo

End
Structure

 

Module
Test

style='mso-spacerun:yes'>    Sub class=SpellE>Foo( class=SpellE>ByVal o style='color:blue'>As Object)

style='mso-spacerun:yes'>        Dim x class=GramE>As Foo

 

style='mso-spacerun:yes'>        If o style='color:blue'>Is Nothing style='color:blue'>Then

style='mso-spacerun:yes'>            x = Foo.DEFAULT

style='mso-spacerun:yes'>        Else

style='mso-spacerun:yes'>            x = class=GramE>DirectCast class=GramE>(o, Foo)

style='mso-spacerun:yes'>        End style='color:blue'>If

 

style='mso-spacerun:yes'>    End style='color:blue'>Sub

End
Module

 

The nice
thing about Option 1 is that you can use this technique for any structure. style='mso-spacerun:yes'>  
However, Option 2 is the better choice
because, assuming you can modify the declaration of the structure, the compiler
will stop you from accidentally modifying the DEFAULT variable, thereby
preserving its clean state.

 

Note that
assigning Nothing
to a structure generates a call to CreateInstance as well. 
You should use these techniques rather than assigning style='mso-bidi-font-weight:normal'>Nothing to a structure:

 

Module
Test

style='mso-spacerun:yes'>    Sub class=SpellE>Foo( class=SpellE>ByVal o style='color:blue'>As Object)

style='mso-spacerun:yes'>        Dim x class=GramE>As Foo

 

style='mso-spacerun:yes'>        x = Nothing style='mso-spacerun:yes'>      ‘Okay

style='mso-spacerun:yes'>        x = class=GramE>Foo.DEFAULT style='mso-spacerun:yes'>  ‘Better style='color:green'>

style='mso-spacerun:yes'>    End style='color:blue'>Sub

End
Module

 

Also note
that these techniques pertain to Structures,
not intrinsic value types such as Byte,
Integer, Long, Double, etc. style='mso-spacerun:yes'>  
Casting from Object to Integer will style='mso-bidi-font-style:normal'>not involve a call to class=SpellE>CreateInstance.

 

We’re
definitely looking at having the compiler use these same techniques (probably
Option 1 where the local will be an inaccessible temporary variable) in the
next version.

 

Comments (2)

  1. Mark Hurd says:

    BUGBUG: In option 1, I’m sure
    x = Foo
    is meant to be
    x = DEFAULT
    🙂

  2. Cameron Beccario says:

    thanks! I’ve updated it.