提问人:genespos 提问时间:11/11/2015 最后编辑:ElektroStudiosgenespos 更新时间:11/12/2015 访问量:5316
使用 array.resize 而不是 redim 的原因
Reasons to use array.resize instead of redim
答:
我不是 100% 确定,但我似乎记得 resize 将直接在数组上工作,而 redim 将以新大小创建一个新数组,然后将值从旧数组复制到新数组中,然后处理旧的并重命名新的数组。即相当多的处理。
评论
正如 MSDN 中所解释的,ReDim 和 Array.Resize 似乎通过分配一个具有固定大小的新 Array 来复制旧 awwat 的内容来工作,但是有一些内部差异(我忽略了)使速度更快,并且在比较时是一个性能杀手。ReDim
ReDim Preserve
通过下一个测试的结果,该测试执行一个简单的迭代以重新调整数组的维度 100.000 次,您可以证明它比 快得多,但是,两者都比使用方法的自定义泛型解决方案慢。ReDim
Array.Resize
Array.Copy
结果
ReDim 运行时间:00:00:07.3918219
Array.Resize 运行时间:00:00:28.9773317
自定义调整大小运行时间:00:00:03.2659880
如您所见,和 之间相差 +20 秒ReDim
Array.Resize
测试源代码
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
免责声明:我不是该函数的作者,它是我前段时间在关于 Array.Resize 性能的随机 StackOverflow 答案中发现的 C# 代码的自定义 Vb.Net 改编。
评论
ResizeArray(myArray, x)
Array.Resize
myArray = ResizeArray(myArray, x)
除非添加修饰符,否则无法比较 ReDim 和 Array.Resize<T>。Preserve
Array.Resize
不仅会分配一个新数组,还会将源数组中的所有项目复制到目标数组。
ReDim
如果没有修饰符,将只分配一个新数组。源数组中的所有项都将丢失。Preserve
所以基本上是这样:
Dim a As String() = {"item1", "item2"}
ReDim a(4 - 1) 'Double the size
'At this point, a contains 4 null references.
...等于:
Dim a As String() = {"item1", "item2"}
a = New String(4 - 1) {} 'Double the size
'At this point, a contains 4 null references.
您可以通过检查发布配置中生成的 CIL 代码并查找 0x8D - newarr <etype> 指令来验证这一点。通过了解这一点,很明显为什么它比 .Array.Resize<T>
因此,让我们比较一下 和 .ReDim Preserve
Array<T>
那么使用哪一个呢?
让我们创建两个方法并查看代码。CIL
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
}
如您所见,最终成为对 Microsoft.VisualBasic.CompilerServices.Utils.CopyArray 的调用指令。现在,如果检查(源代码在 RS 上不可用)和 Array.Resize<T>,您将看到这两个方法最终都是对 Array.Copy<T> 的调用指令,这是一个 CLR 方法。ReDim Preserve
CopyArray
因此,人们可以争辩说它们本质上都是“相同的”,而快速的 benckmark(可在本文末尾找到)似乎证实了这一点。
但是,正如 Hans Passant 正确指出的那样,每当需要操作数组时>都应该使用 List<T。
基准
迭代次数:10
MaxSize:100000空源数组:
{ 方法 = ResizeArray1, Time = 00:00:05.6533126 }
{ 方法 = ResizeArray2, Time = 00:00:05.6973607 }不断增长的源阵列:
{ 方法 = ResizeArray1, Time = 00:01:42.6964858 }
{ 方法 = 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
评论
Array.Copy
评论