.NETのボックス化は遅いのか?
はじめに
ちょっとした用事でVBの遅延バインディングが遅い上にOption Strict Off
じゃないと使えないというやっぱりVBはクソVBはこういうところで小回りが利かないな〜という問題に対して、自前で遅延バインディングを実装してみればいいんじょないかとマリーアントワネット的な解決を図っていました。
そうなるとボックス化とボックス化の解除を割とカジュアルにやらなきゃいけなかったのですが、こうなるとどのくらいパフォーマンスに影響が出るのかな〜と気になって調べた時の記録です。
パフォーマンステスト
パフォーマンステストといっても弊社の環境はVM上のWindowsなのであまりアテにならないと思います。 が、一応こんな感じです。
- MacBook Pro Mid 2012上で動くYosemite上で動くVMWare Fusion Pro 8上で動くWindows 8.1 Pro
- Visual Studio 2015 Update 1
- .NET 4.5.2
そんでもってこんな感じのメソッドを用意します。
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場合ではボックス化・ボックス化解除による不必要なオーバーヘッドが発生しないようにするべきなのも確かです。
おわりに
余談なんですが、ボックス化・ボックス化解除は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
となり倍近く遅くなります。
CType
とDirectCast
の違いについて解説している記事はたくさんあるのでここでは割愛しますが、こんな感じでコンパイル結果に反映されているんだな〜程度に知ってもらえるだけでも何か良いと思います。
おわり