哪个更快? ByVal还是ByRef?

时间:2009-01-02 21:45:44

标签: vb.net performance byref byval

在VB.NET中,方法参数的使用速度更快,ByValByRef

此外,它在运行时(RAM)消耗更多资源?


我通读this question,但答案不适用或不够具体。

7 个答案:

答案 0 :(得分:122)

Byval和ByRef参数应该根据他们如何在 上工作的要求和知识来使用。

http://www.developer.com/net/vb/article.php/3669066

回应斯劳的评论 -

哪个在运行时消耗更多资源?

参数在堆栈上传递。堆栈非常快,因为它的内存分配只是一个指针增量,用于保留新的“帧”或“分配记录”。大多数.NET参数都不超过机器寄存器的大小,如果使用任何“堆栈”空间来传递参数那么少。实际上,基本类型和指针都在堆栈上分配。 .NET中的堆栈大小限制为1 MB。这可以让您了解参数传递消耗的资源很少。

您可能会发现这一系列文章很有趣:

Improving Performance Through Stack Allocation (.NET Memory Management: Part 2)

哪个更快? ByVal或ByRef。

根据测量的背景,很难准确地测量和精灵测量,但是我写了一个基准,我用1亿次调用​​方法得出以下信息:

  • 参考类型 - 通过参考:420毫秒
  • 参考类型 - 通过ByVal:382 ms
  • 值类型 - 通过ByRef:421 ms
  • 值类型 - 通过ByVal:416 ms
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

在每种方法中注释掉变量和赋值 -

  • 参考类型 - 通过ByRef:389 ms
  • 参考类型 - 通过ByVal:349 ms
  • 值类型 - 通过参考:416毫秒
  • 值类型 - 通过ByVal:385 ms

可以得出结论,传递引用类型(字符串,类)ByVal将节省一些时间。您可能还会说传递值类型(整数,字节) - ByVal将节省一些时间。

在宏伟的计划中,时间可以忽略不计。更重要的是正确使用ByVal和ByRef并了解“幕后”的情况。在您的例程中实现的算法肯定会多次影响程序的运行时间。

答案 1 :(得分:30)

如果您使用的是非常大的值类型(例如,Guid非常大),通过引用传递参数可能会稍微快一点。在其他情况下,当您通过引用传递而不是按值传递时,可能会有更多复制等 - 例如,如果您有一个字节参数,那么一个字节显然小于四个或八个字节如果您通过引用传递它,指针将采用。

在实践中,你几乎不应该担心这一点。尽可能编写可读代码,这几乎总是意味着按值而不是引用传递参数。我很少使用ByRef。

如果您想提高效果并认为ByRef会对您有所帮助,在提交之前仔细(根据您的具体情况)对其进行基准测试。

编辑:我在评论中注意到另一个(先前已被接受,现已删除)的答案,关于ByRef vs ByVal在价值类型方面的含义存在很多误解。我有一个article about parameter passing多年来一直很受欢迎 - 它是用C#术语,但同样的概念适用于VB.NET。

答案 2 :(得分:11)

这取决于。如果要传递一个对象,它已经传递了一个指针。这就是为什么如果你传入一个ArrayList(例如)并且你的方法将一些东西添加到ArrayList,那么调用代码也有相同的对象进入它的ArrayList,传入,因为它是相同的ArrayList。它没有传递指针的唯一时间是将具有内部数据类型的变量(如int或double)传递给函数。此时,它会创建一个副本。但是,这些对象的数据大小非常小,在内存使用或执行速度方面几乎没有任何区别。

答案 3 :(得分:5)

如果传入引用类型,则ByRef较慢。

这是因为传入的是指向指针的指针。对对象上的字段的任何访问都需要取消引用额外的指针,这将需要几个额外的时钟周期才能完成。

如果要传递值类型,那么如果结构有许多成员,则byref可能会更快,因为它只传递单个指针而不是复制堆栈上的值。在访问成员方面,byref会更慢,因为它需要进行额外的指针取消引用(sp-> pValueType-> member vs sp-> member)。

大部分时间在VB中你不必担心这个。

在.NET中,很少有具有大量成员的值类型。它们通常很小。在这种情况下,传入值类型与向过程传递多个参数没有什么不同。例如,如果您有按值传递Point对象的代码,则它的perf将与将X和Y值作为参数的方法相同。看到DoSomething(x为整数,y为整数)可能不会导致perf问题。事实上,你可能永远不会三思而后行。

如果您要定义自己的大值类型,那么您应该重新考虑将它们转换为引用类型。

唯一的另一个区别是执行代码所需的指针间接数量的增加。您很少需要在该级别进行优化。 大多数情况下,您可以解决算法问题,或者您的性能瓶颈与IO相关,例如等待数据库或写入文件,在这种情况下,消除指针间接对您没有多大帮助。 / p>

因此,我不建议你应该专注于为你提供所需语义的东西,而不是专注于byter byval或byref。一般来说,除非你特别需要byref,否则最好使用byval。它使程序更容易理解。

答案 4 :(得分:2)

虽然我对.NET的内部知识不太了解,但我将讨论我对编译语言的了解。此不适用于引用类型,可能对值类型不完全准确。如果您不知道值类型和引用类型之间的区别,则不应阅读此内容。我假设32位x86(带32位指针)。

  • 传递小于32位的值仍然在堆栈上使用32位对象。该对象的一部分将是“未使用”或“填充”。传递这些值并不比使用32位值使用更少的内存。
  • 传递大于32位的值将使用比指针更多的堆栈空间,并且可能会有更多的复制时间。
  • 如果按值传递对象,则被调用者可以从堆栈中获取对象。如果通过引用传递对象,则被调用者必须首先从堆栈中获取对象的地址,然后从其他地方获取对象的值。按价值意味着少拿一个,对吗?好吧,实际上fetch需要由调用者完成 - 但是调用者可能已经因为不同的原因而必须获取,在这种情况下保存了一个fetch。
  • 显然,对引用参数值所做的任何更改都必须保存回RAM,而可以放弃按值参数。
  • 最好通过值传递,而不是仅通过引用传递将参数复制到局部变量而不再触摸它。

判决结果:

了解ByVal和ByRef实际上为您做了什么,理解值和引用类型之间的区别,而不是考虑性能,这一点更为重要。第一条规则是使用更适合您代码的方法

对于大值类型(超过64位),按引用传递,除非通过值传递有利(例如更简单的代码,“只是有意义”或接口一致性。)

对于较小的值类型,传递机制对性能没有太大影响,无论如何很难预测哪种方法会更快,因为它取决于对象大小,调用者和被调用者如何使用该对象,以及甚至缓存注意事项只需对你的代码做任何有意义的事情。

答案 5 :(得分:1)

ByVal创建变量的副本,而ByRef传递指针。因此,我会说ByVal较慢(由于复制所需的时间)并使用更多内存。

答案 6 :(得分:0)

我的好奇心是根据对象和记忆用法来检查不同的行为

结果似乎证明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