使用array.resize而不是redim的原因

时间:2015-11-11 07:59:27

标签: .net arrays vb.net

我知道“redim”比“Array.Resize”旧,但不明白将后者用于前者的原因。

3 个答案:

答案 0 :(得分:2)

正如MSDN中所解释的, ReDim Array.Resize 似乎通过分配一个固定大小的新数组来复制内容旧的awwat,但是有一些内部差异(我忽略)使得ReDim更快,而ReDim Preserve是性能杀手。

通过下一个测试的结果,它执行一个简单的迭代来重新定义一个数组100.000次,你可以证明ReDimArray.Resize快得多,但是,两者都比自定义慢使用Array.Copy方法的通用解决方案。

结果

  

ReDim经历时间:00:00:07.3918219

     

Array.Resize Elapsed Time:00:00:28.9773317

     

自定义调整已用时间:00:00:03.2659880

如您所见,ReDimArray.Resize

之间的差异为+20秒

测试源代码

Dim myArray As String()
Dim sw As New Stopwatch

myArray = New String() {"Q", "W", "E", "R", "T", "Y"}
sw.Start()
For x As Integer = 1 To 100000 : ReDim myArray(x) : Next
sw.Stop()
Console.WriteLine("ReDim Elapsed Time: " & sw.Elapsed.ToString)

myArray = New String() {"Q", "W", "E", "R", "T", "Y"}
sw.Restart()
For x As Integer = 1 To 100000 : Array.Resize(myArray, x) : Next
sw.Stop()
Console.WriteLine("Array.Resize Elapsed Time: " & sw.Elapsed.ToString)

myArray = New String() {"Q", "W", "E", "R", "T", "Y"}
sw.Restart()
For x As Integer = 1 To 100000 : ResizeArray(myArray, x) : Next
sw.Stop()
Console.WriteLine("Custom Resize Elapsed Time: " & sw.Elapsed.ToString)

自定义阵列 - 调整大小解决方案:

将其添加为扩展方法非常有用。

' <Extension>
<DebuggerStepThrough>
Public Function ResizeArray(Of T)(ByVal sender As T(),
                                  ByVal newSize As Integer) As T()

    If (newSize <= 0) Then
        Throw New ArgumentOutOfRangeException(paramName:="newSize",
                                              message:="Value bigger than 0 is required.")
    End If

    Dim preserveLength As Integer = Math.Min(sender.Length, newSize)

    If (preserveLength > 0) Then
        Dim newArray As Array = Array.CreateInstance(sender.GetType.GetElementType, newSize)
        Array.Copy(sender, newArray, preserveLength)
        Return DirectCast(newArray, T())

    Else
        Return sender

    End If

End Function

免责声明:我不是该函数的作者,它是来自C#代码的自定义Vb.Net改编版,我在之前发现的随机StackOverflow答案中有关Array.Resize的性能。

答案 1 :(得分:1)

You cannot possibly compare ReDim and Array.Resize<T> unless you add the Preserve modifier.

Array.Resize will not only allocate a new array, but also copies all the items in the source array to the target array.

ReDim without the Preserve modifier will only allocate a new array. All the items in the source array is lost.

So basicly this:

Dim a As String() = {"item1", "item2"}
ReDim a(4 - 1) 'Double the size
'At this point, a contains 4 null references.

... is equal to this:

Dim a As String() = {"item1", "item2"}
a = New String(4 - 1) {} 'Double the size
'At this point, a contains 4 null references.

You can verify this by inspecting the produced CIL code from release configuration and look for the 0x8D - newarr <etype> instruction. By knowing this it's quite obvious as to why this is faster than Array.Resize<T>.

So instead, let's compare ReDim Preserve and Array<T>.

So which one to use?

Let's create two methods and look at the CIL code.

VB.NET

Private Sub ResizeArray1(Of T)(ByRef a As T(), size As Int32)
    ReDim Preserve a(size - 1)
End Sub

Private Sub ResizeArray2(Of T)(ByRef a As T(), size As Int32)
    Array.Resize(a, size)
End Sub

CIL

.method private static void ResizeArray1<T>(!!T[]& a, int32 size) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.0 
    L_0002: ldind.ref 
    L_0003: ldarg.1 
    L_0004: ldc.i4.1 
    L_0005: sub.ovf 
    L_0006: ldc.i4.1 
    L_0007: add.ovf 
    L_0008: newarr !!T
    L_000d: call class [mscorlib]System.Array [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Utils::CopyArray(class [mscorlib]System.Array, class [mscorlib]System.Array)
    L_0012: castclass !!T[]
    L_0017: stind.ref 
    L_0018: ret 
}

.method private static void ResizeArray2<T>(!!T[]& a, int32 size) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: call void [mscorlib]System.Array::Resize<!!T>(!!0[]&, int32)
    L_0007: ret 
}

As you can see ReDim Preserve ends up as a call instruction to Microsoft.VisualBasic.CompilerServices.Utils.CopyArray. Now, if you inspect CopyArray (source code not available at RS) and Array.Resize<T> you'll see that both methods ends up as a call instruction to Array.Copy<T> which is a CLR method.

So one can argue that they are both essentially "identical", and a quick benckmark (available at the end of this post) seems to confirm this.

However, as correctly noted by Hans Passant, you should rather be using a List<T> whenever you need to manipulate an array.


Benchmark

Iterations: 10
MaxSize: 100000

Empty source array:

{ Method = ResizeArray1, Time = 00:00:05.6533126 }
{ Method = ResizeArray2, Time = 00:00:05.6973607 }

Growing source array:

{ Method = ResizeArray1, Time = 00:01:42.6964858 }
{ Method = ResizeArray2, Time = 00:01:42.1891668 }

Option Strict On

Public Module Program

    Friend Sub Main()
        Console.WriteLine("Warming up...")
        Program.Benchmark(iterations:=10, maxSize:=1000, warmUp:=True)
        Console.WriteLine("Warmup completed. Measurement started...")
        Program.Benchmark(iterations:=10, maxSize:=100000, warmUp:=False)
        Console.WriteLine()
        Console.WriteLine("Measurement completed. Press any key to exit.")
        Console.ReadKey()
    End Sub

    Private Sub Benchmark(iterations As Int32, maxSize As Int32, warmUp As Boolean)

        Dim watch As Stopwatch
        Dim a As String()

        'BY: EMPTY SOURCE ARRAY --------------------------------- 

        'Resize array #1

        watch = Stopwatch.StartNew()

        For i As Int32 = 1 To iterations
            For n As Int32 = 1 To maxSize
                a = Program.CreateArray(Of String)(0)
                Program.ResizeArray1(a, n)
            Next
        Next

        watch.Stop()

        If (Not warmUp) Then
            Console.WriteLine()
            Console.WriteLine(String.Format("R E S U L T"))
            Console.WriteLine()
            Console.WriteLine(String.Format("Iterations: {0}", iterations))
            Console.WriteLine(String.Format("   MaxSize: {0}", maxSize))
            Console.WriteLine()
            Console.WriteLine("Empty source array:")
            Console.WriteLine()
            Console.WriteLine(New With {.Method = "ResizeArray1", .Time = watch.Elapsed.ToString()})
        End If

        'Resize array #2

        watch = Stopwatch.StartNew()

        For i As Int32 = 1 To iterations
            For n As Int32 = 1 To maxSize
                a = CreateArray(Of String)(0)
                ResizeArray2(a, n)
            Next
        Next

        watch.Stop()

        If (Not warmUp) Then
            Console.WriteLine(New With {.Method = "ResizeArray2", .Time = watch.Elapsed.ToString()})
        End If

        'BY: GROWING SOURCE ARRAY -------------------------------

        'Resize array #1

        watch = Stopwatch.StartNew()
        a = Program.CreateArray(Of String)(0)

        For i As Int32 = 1 To iterations
            For n As Int32 = 1 To maxSize
                Program.ResizeArray1(a, n)
            Next
        Next

        watch.Stop()

        If (Not warmUp) Then
            Console.WriteLine()
            Console.WriteLine("Growing source array:")
            Console.WriteLine()
            Console.WriteLine(New With {.Method = "ResizeArray1", .Time = watch.Elapsed.ToString()})
        End If

        'Resize array #2

        watch = Stopwatch.StartNew()
        a = Program.CreateArray(Of String)(0)

        For i As Int32 = 1 To iterations
            For n As Int32 = 1 To maxSize
                Program.ResizeArray2(a, n)
            Next
        Next

        watch.Stop()

        If (Not warmUp) Then
            Console.WriteLine(New With {.Method = "ResizeArray2", .Time = watch.Elapsed.ToString()})
        End If

    End Sub

    Private Function CreateArray(Of T)(size As Int32) As T()
        Return New T(size - 1) {}
    End Function

    Private Sub ResizeArray1(Of T)(ByRef a As T(), size As Int32)
        ReDim Preserve a(size - 1)
    End Sub

    Private Sub ResizeArray2(Of T)(ByRef a As T(), size As Int32)
        Array.Resize(a, size)
    End Sub

End Module

答案 2 :(得分:0)

我不是百分百肯定,但我似乎记得resize将直接在数组上工作,而redim将以新的大小创建一个新数组,然后将值从old复制到new,然后处理旧的和重命名新的。即更多的处理。