.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

メモリにやさしい空配列の取得を学ぶ

元ネタはこちら。

C#の記事なので、これをVB.NETのコードに変えると空の配列は

'A
Dim ary = DirectCast(Enumerable.Empty(Of Integer)(), Integer())

このように書くことでメモリが無駄に使われないとのこと。 ちなみに私は

'B
Dim ary = DirectCast({}, IEnumerable(Of Integer))

こう書いてました。*1

で元記事のコメントにて、速度やメモリ使用量について議論があったようなので、調べてみます。

3億ループ時の比較

ロジック 速度(msec) メモリ使用量(MB)
A 18,000 0.008
B 6,793 1.273

メモリは記事の通り、Aが圧倒的に少ないことは確認できます。*2しかし、コメントにあるとおりAが遅い。

Aのコードが遅い理由

個人的にはわざわざInteger()でなければいけないケースはなさそうだし、IEnumerable(Of Integer)のままでいいから、

'C
Enumerable.Empty(Of Integer)()

に変えて計測してみたら、

ロジック 速度(msec) メモリ使用量(MB)
A 18,000 0.008
B 6,793 1.273
C 6,134 0.008

になりました。つまり、型変換に時間かかってただけという…

以下、速度計測に使用したコード。検証用なので汚いです。

Sub Main()
    Dim cnt = 300000000

    Dim sw As New Stopwatch

    Dim before = System.GC.GetTotalMemory(True)
    sw.Start()
    For i As Integer = 1 To cnt
        Dim ary = New Integer(-1) {}
    Next
    sw.Stop()
    Console.WriteLine("Dim ary = New Integer(-1) {}")
    Console.WriteLine(String.Format("{0:n0}ms", sw.ElapsedMilliseconds))
    Console.WriteLine(String.Format("{0:n3}MB used", (System.GC.GetTotalMemory(False) - before) / 1024 / 1024))
    sw.Reset()

    before = System.GC.GetTotalMemory(True)
    sw.Start()
    For i As Integer = 1 To cnt
        Dim ary = DirectCast({}, IEnumerable(Of Integer))
    Next
    sw.Stop()
    Console.WriteLine("Dim ary = DirectCast({}, IEnumerable(Of Integer))")
    Console.WriteLine(String.Format("{0:n0}ms", sw.ElapsedMilliseconds))
    Console.WriteLine(String.Format("{0:n3}MB used", (System.GC.GetTotalMemory(False) - before) / 1024 / 1024))
    sw.Reset()


    before = System.GC.GetTotalMemory(True)
    sw.Start()
    For i As Integer = 1 To cnt
        Dim ary = DirectCast(Enumerable.Empty(Of Integer)(), Integer())
    Next
    sw.Stop()
    Console.WriteLine("Dim ary = DirectCast(Enumerable.Empty(Of Integer)(), Integer())")
    Console.WriteLine(String.Format("{0:n0}ms", sw.ElapsedMilliseconds))
    Console.WriteLine(String.Format("{0:n3}MB used", (System.GC.GetTotalMemory(False) - before) / 1024 / 1024))
    sw.Reset()

    before = System.GC.GetTotalMemory(True)
    sw.Start()
    For i As Integer = 1 To cnt
        Dim ary = Enumerable.Empty(Of Integer)()
    Next
    sw.Stop()
    Console.WriteLine("Dim ary = Enumerable.Empty(Of Integer)()")
    Console.WriteLine(String.Format("{0:n0}ms", sw.ElapsedMilliseconds))
    Console.WriteLine(String.Format("{0:n3}MB used", (System.GC.GetTotalMemory(False) - before) / 1024 / 1024))

    Console.WriteLine("end.")
    Console.ReadKey()
End Sub

*1:元ネタに書いてあるコードとは異なりますが、パフォーマンスはほぼ一緒だったのでこれでを比較対象にします。

*2:空配列なんだから当たり前といえば当たり前

. .