SYSK 370: The Performance Cost of Extension Methods
First, for those who may not be familiar with this feature, .NET 3.5 allows developers to add methods to existing types without using inheritance or partial classes by creating static methods that can be invoked by using instance method syntax.
When I first heard about this feature, it reminded me of the decorator pattern in OOP (I’m not speaking of the implementation, but the concept… )
Since there are lots of examples for C# developers, and very few for VB.NET, here is an example of adding Serialize and Deserialize methods to any object written in VB.NET:
1. Add a module file to your project and paste the following code:
' TODO: need to add SEH and NULL handling!
' TODO: Check for object being marked with Serializable attribute
Module Module1
<System.Runtime.CompilerServices.Extension()> _
Public Function Serialize(ByVal o As Object) As String
Dim data() As Byte
Using ms As New System.IO.MemoryStream()
Dim f As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
f.Serialize(ms, o)
data = ms.ToArray
End Using
Return Convert.ToBase64String(data)
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function Deserialize(ByVal s As String) As Object
Dim result As Object
Dim data() As Byte = Convert.FromBase64String(s)
Using ms As New System.IO.MemoryStream(data)
Dim f As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
result = f.Deserialize(ms)
End Using
Return result
End Function
End Module
2. Below is an example of using this extension method (the important lines are in bold):
Public Class Form2
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t As New Test
t.i = Integer.Parse(Me.TextBox1.Text)
t.s = Me.TextBox2.Text
Me.TextBox3.Text = t.Serialize()
End Sub
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.TextBox1.Text = New Random().Next(1, 1000).ToString()
Me.TextBox2.Text = "Time now is " + DateTime.Now().ToLongTimeString()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim t As Test = Me.TextBox3.Text.Deserialize()
Me.TextBox4.Text = t.i.ToString()
Me.TextBox5.Text = t.s
End Sub
End Class
<System.Serializable()> _
Public Class Test
Private _s As String
Public Property s() As String
Get
Return _s
End Get
Set(ByVal value As String)
_s = value
End Set
End Property
Private _n As New NestedTest
Public Property i() As Integer
Get
Return _n.i
End Get
Set(ByVal value As Integer)
_n.i = value
End Set
End Property
End Class
<System.Serializable()> _
Public Class NestedTest
Private _i As Integer
Public Property i() As Integer
Get
Return _i
End Get
Set(ByVal value As Integer)
_i = value
End Set
End Property
End Class
Now, to the promised performance impact… I ran and timed a test that created 100,000,000 instances of an object with the extension methods above, and compared the execution time to doing the same but without the extension methods. Just to be clear, I didn’t actually execute the extension methods… My goal was to find out if simply adding the extension methods would significantly impact the object instantiation time by running the following code:
Dim t As New System.Diagnostics.Stopwatch
t.Start()
Dim o As Test
Dim i As Integer
For i = 0 To 100000000
o = New Test
o.i = 1
o = Nothing
Next
t.Stop()
System.Diagnostics.Debug.WriteLine(t.ElapsedMilliseconds().ToString())
To my surprise, my tests show that adding extension methods did not result in any measurable performance decrease. On my dual proc laptop with 4 Gb RAM, the results were as follows:
With Extension Methods Without Extension Methods
--------------------------------------------------------------------------------------
Run 1 4071 ms 3905 ms
Run 2 4118 ms 3916 ms
Run 3 4145 ms 3904 ms
Bottom line – it appears that using extension methods does not have any measurable performance hit on object instantiation time.
Comments
- Anonymous
June 25, 2009
I'm not sure why this would be surprising. The extended class is completely decoupled from the extension method. e.g. the compilation of the extended class could have have occured before the existence of the extension method. Therefore instantiation code must not be affected by an extension method because it can be compiled before the extension method is created.