【发布时间】:2010-09-29 07:35:23
【问题描述】:
【问题讨论】:
-
另一个回复可能是:如果 ByVal / ByRef 选择对应用程序速度变得至关重要,那么可能是时候减少函数调用了...
标签: vb.net performance byref byval
【问题讨论】:
标签: vb.net performance byref byval
Byval 和 ByRef 参数的使用应基于需求和对它们如何工作的了解而不是速度。
http://www.developer.com/net/vb/article.php/3669066
回应 Slough 的评论 -
哪个在运行时消耗更多资源?
参数在堆栈上传递。堆栈非常快,因为它的内存分配只是一个指针增量,以保留新的“帧”或“分配记录”。如果使用任何“堆栈”空间来传递参数,大多数 .NET 参数不会超过机器寄存器的大小。事实上,基本类型和指针都是在堆栈上分配的。 .NET 中的堆栈大小限制为 1 MB。这应该让您了解参数传递消耗的资源有多少。
您可能会发现这一系列文章很有趣:
Improving Performance Through Stack Allocation (.NET Memory Management: Part 2)
哪个更快? ByVal 或 ByRef。
很难准确地进行测量 - 取决于您测量的上下文,但我编写的一个基准测试调用了 1 亿次方法得出了以下结果:
Public Sub Method1(ByRef s As String)
Dim c As String = s
End Sub
Public Sub Method2(ByVal s As String)
Dim c As String = s
End Sub
Public Sub Method3(ByRef i As Integer)
Dim x As Integer = i
End Sub
Public Sub Method4(ByVal i As Integer)
Dim x As Integer = i
End Sub
Sub Main()
Dim s As String = "Hello World!"
Dim k As Integer = 5
Dim t As New Stopwatch
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method1(s)
Next
t.Stop()
Console.WriteLine("Reference Type - ByRef " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method2(s)
Next
t.Stop()
Console.WriteLine("Reference Type - ByVal " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method3(i)
Next
t.Stop()
Console.WriteLine("Value Type - ByRef " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method4(i)
Next
t.Stop()
Console.WriteLine("Value Type - ByVal " & t.ElapsedMilliseconds)
Console.ReadKey()
End Sub
注释掉每个方法中的变量和赋值-
可以得出结论,通过 ByVal 传递引用类型(字符串、类)将节省一些时间。您也可能会说传递值类型(整数、字节) - ByVal 会节省一些时间。
再一次,时间在宏伟的计划中可以忽略不计。更重要的是正确使用 ByVal 和 ByRef 并了解“幕后”发生的事情。在你的例程中实现的算法肯定会影响你程序的运行时间很多倍。
【讨论】:
ByVal 所需的时间将根据相关值类型的大小而有所不同。通过ByRef所需的时间不会。如果值类型很大,传递ByRef 可能会快得多。请注意,如果可以避免按值传递或不必要地复制它们,那么即使非常大的值类型(数千字节)也可以非常高效。
Byte() 数组,ByVal 比ByRef 快,但也可以忽略不计。我修改了这个测试以使用 Byte() 而不是 String 并使用 6 MB 字节数组运行它。 ByRef 是 331 毫秒,ByVal 是 231 毫秒。这可以忽略不计:每次调用 0.000001 毫秒的差异。
如果您使用非常大的值类型(例如,Guid 非常大),通过引用传递参数可能会稍微快一些。在其他情况下,当您通过引用而不是通过值传递时,可能会有更多复制等 - 例如,如果您有一个字节参数,那么一个字节显然小于四个或八个字节如果您通过引用传递指针,则该指针将采用。
在实践中,您几乎不必担心这一点。编写尽可能可读的代码,这几乎总是意味着通过值而不是引用传递参数。我很少使用 ByRef。
如果您想提高性能并认为 ByRef 会帮助您,请在承诺之前仔细(在您的具体情况下)对其进行基准测试。
编辑:我在 cmets 中注意到另一个(以前接受,现在已删除)的答案,当涉及到值类型时,对于 ByRef 与 ByVal 的含义存在很多误解。我有一个article about parameter passing,多年来证明它很受欢迎——它是用 C# 术语表示的,但同样的概念也适用于 VB.NET。
【讨论】:
这取决于。如果您正在传递一个对象,那么它已经在传递一个指针。这就是为什么如果你传入一个 ArrayList(例如)并且你的方法向 ArrayList 添加了一些东西,那么调用代码也会在传入的 ArrayList 中有相同的对象,因为它是相同的 ArrayList。唯一不传递指针的情况是,当您将具有内在数据类型(如 int 或 double)的变量传递给函数时。此时,它会创建一个副本。但是,这些对象的数据量非常小,无论是在内存使用量还是执行速度方面都几乎没有什么区别。
【讨论】:
如果传入引用类型,ByRef 会更慢。
这是因为传入的是指向指针的指针。对对象上字段的任何访问都需要取消引用一个额外的指针,这将需要几个额外的时钟周期才能完成。
如果你传递一个值类型,那么如果结构有很多成员,那么 byref 可能会更快,因为它只传递一个指针而不是复制堆栈上的值。在访问成员方面,byref 会慢一些,因为它需要做额外的指针解引用(sp->pValueType->member vs sp->member)。
在 VB 中的大多数情况下,您不必担心这一点。
在 .NET 中,很少有具有大量成员的值类型。它们通常很小。在这种情况下,传入一个值类型与将多个参数传入一个过程没有什么不同。例如,如果您的代码按值传入 Point 对象,则其性能与将 X 和 Y 值作为参数的方法相同。看到 DoSomething(x as integer, y as integer) 可能不会引起性能问题。事实上,你可能永远不会三思而后行。
如果您自己定义大值类型,那么您可能应该重新考虑将它们转换为引用类型。
唯一的其他区别是执行代码所需的指针间接次数增加。您很少需要在该级别进行优化。 大多数时候,您可以解决算法问题,或者您的性能瓶颈与 IO 相关,例如等待数据库或写入文件,在这种情况下,消除指针间接对您没有太大帮助。
因此,与其关注 byval 或 byref 哪个更快,我建议您真正关注什么为您提供所需的语义。一般来说,除非您特别需要 byref,否则使用 byval 是个好主意。它使程序更容易理解。
【讨论】:
虽然我不太了解 .NET 的内部结构,但我将讨论我对编译语言的了解。这不适用于引用类型,并且对于值类型可能并不完全准确。如果您不知道值类型和引用类型之间的区别,则不应阅读此内容。我假设是 32 位 x86(带有 32 位指针)。
判决:
了解 ByVal 和 ByRef 实际为您做了什么以及了解值类型和引用类型之间的区别比考虑性能要重要得多。第一条规则是使用更适合您的代码的方法。
对于大值类型(超过 64 位),按引用传递,除非按值传递有优势(例如更简单的代码、“它才有意义”或接口一致性)。
对于较小的值类型,传递机制对性能没有太大影响,而且无论如何很难预测哪种方法会更快,因为它取决于对象大小、调用者和被调用者如何使用对象,以及甚至缓存注意事项。做任何对你的代码有意义的事情。
【讨论】:
ByVal 创建变量的副本,而ByRef 传递一个指针。因此我会说ByVal 更慢(由于复制需要时间)并且使用更多内存。
【讨论】:
我的好奇心是根据对象和内存使用情况检查不同的行为
结果似乎表明 ByVal 总是获胜,资源取决于是否收集内存或更少(仅限 4.5.1)
Public Structure rStruct
Public v1 As Integer
Public v2 As String
End Structure
Public Class tClass
Public v1 As Integer
Public v2 As String
End Class
Public Sub Method1(ByRef s As String)
Dim c As String = s
End Sub
Public Sub Method2(ByVal s As String)
Dim c As String = s
End Sub
Public Sub Method3(ByRef i As Integer)
Dim x As Integer = i
End Sub
Public Sub Method4(ByVal i As Integer)
Dim x As Integer = i
End Sub
Public Sub Method5(ByVal st As rStruct)
Dim x As rStruct = st
End Sub
Public Sub Method6(ByRef st As rStruct)
Dim x As rStruct = st
End Sub
Public Sub Method7(ByVal cs As tClass)
Dim x As tClass = cs
End Sub
Public Sub Method8(ByRef cs As tClass)
Dim x As tClass = cs
End Sub
Sub DoTest()
Dim s As String = "Hello World!"
Dim cs As New tClass
cs.v1 = 1
cs.v2 = s
Dim rt As New rStruct
rt.v1 = 1
rt.v2 = s
Dim k As Integer = 5
ListBox1.Items.Add("BEGIN")
Dim t As New Stopwatch
Dim gt As New Stopwatch
If CheckBox1.Checked Then
ListBox1.Items.Add("Using Garbage Collection")
System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.GetTotalMemory(False)
End If
Dim d As Double = GC.GetTotalMemory(False)
ListBox1.Items.Add("Free Memory: " & d)
gt.Start()
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method1(s)
Next
t.Stop()
ListBox1.Items.Add("Reference Type - ByRef " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method2(s)
Next
t.Stop()
ListBox1.Items.Add("Reference Type - ByVal " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method3(i)
Next
t.Stop()
ListBox1.Items.Add("Value Type - ByRef " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method4(i)
Next
t.Stop()
ListBox1.Items.Add("Value Type - ByVal " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method5(rt)
Next
t.Stop()
ListBox1.Items.Add("Structure Type - ByVal " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method6(rt)
Next
t.Stop()
ListBox1.Items.Add("Structure Type - ByRef " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method7(cs)
Next
t.Stop()
ListBox1.Items.Add("Class Type - ByVal " & t.ElapsedMilliseconds)
t.Reset()
t.Start()
For i As Integer = 0 To 100000000
Method8(cs)
Next
t.Stop()
gt.Stop()
ListBox1.Items.Add("Class Type - ByRef " & t.ElapsedMilliseconds)
ListBox1.Items.Add("Total time " & gt.ElapsedMilliseconds)
d = GC.GetTotalMemory(True) - d
ListBox1.Items.Add("Total Memory Heap consuming (bytes)" & d)
ListBox1.Items.Add("END")
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DoTest()
End Sub
【讨论】: