.NETのボックス化は遅いのか?

はじめに

ちょっとした用事でVBの遅延バインディングが遅い上にOption Strict Offじゃないと使えないというやっぱりVBはクソVBはこういうところで小回りが利かないな〜という問題に対して、自前で遅延バインディングを実装してみればいいんじょないかとマリーアントワネット的な解決を図っていました。

そうなるとボックス化とボックス化の解除を割とカジュアルにやらなきゃいけなかったのですが、こうなるとどのくらいパフォーマンスに影響が出るのかな〜と気になって調べた時の記録です。

パフォーマンステスト

パフォーマンステストといっても弊社の環境はVM上のWindowsなのであまりアテにならないと思います。 が、一応こんな感じです。

そんでもってこんな感じのメソッドを用意します。

Function AddInt(x As Integer, y As Integer) As Integer
    Return x + y
End Function

Function AddBox(x As Object, y As Object) As Integer
    Return DirectCast(x, Integer) + DirectCast(y, Integer)
End Function

そしてこんな感じでぶん回します。

Dim testData = New List(Of Tuple(Of Integer, Integer))
Dim rand = New Random()
For i = 1 To 10000000
    testData.Add(Tuple.Create(rand.Next(5), rand.Next(5)))
Next

Dim s1 = New Stopwatch()
s1.Start()
For Each it In testData
    AddInt(it.Item1, it.Item2)
Next
s1.Stop()
Console.WriteLine(s1.ElapsedMilliseconds)

Dim s2 = New Stopwatch()
s2.Start()
For Each it In testData
    AddBox(it.Item1, it.Item2)
Next
s2.Stop()
Console.WriteLine(s2.ElapsedMilliseconds)

そして、こうなります。

165
354

たしかにボックス化で遅くなりましたが、正直リフレクションで生じるオーバーヘッドに比べればはるかに可愛いレベルです。 が、確実に遅くなるので普通にプログラムを書く*1場合ではボックス化・ボックス化解除による不必要なオーバーヘッドが発生しないようにするべきなのも確かです。

パフォーマンス (C# および Visual Basic)

おわりに

余談なんですが、ボックス化・ボックス化解除はILレベルで行われており、それを行うオペコードがあります。

box [mscorlib]System.Int32

とか

unbox.any [mscorlib]System.Int32

とかですね。

ここでAddBoxのキャストをDirectCastからCTypeに変えると

unbox.any [mscorlib]System.Int32

call int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToInteger(object)

になります。

この状態でテストすると

181
648

となり倍近く遅くなります。

CTypeDirectCastの違いについて解説している記事はたくさんあるのでここでは割愛しますが、こんな感じでコンパイル結果に反映されているんだな〜程度に知ってもらえるだけでも何か良いと思います。

おわり

*1:メタプログラミング・リフレクションを使わない場合