哪个更快? ByVal还是ByRef?

时间:2020-12-07 13:54:53

In VB.NET, which is faster to use for method arguments, ByVal or ByRef?

在VB.NET中,它更快地用于方法参数,ByVal或ByRef?

Also, which consumes more resources at runtime (RAM)?

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


I read through this question, but the answers are not applicable or specific enough.

我仔细阅读了这个问题,但答案不适用或不够具体。

7 个解决方案

#1


Byval and ByRef arguments should be used based on requirements and knowledge of how they work not on speed.

Byval和ByRef参数应该根据它们如何工作而不是速度的要求和知识来使用。

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

In response to a comment by Slough -

回应斯劳的评论 -

Which consumes more resources at runtime?

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

Parameters are passed on the stack. The stack is very fast, because its memory allocation is simply a pointer increment to reserve a new "frame" or "allocation record." Most .NET parameters do not exceed the size of a machine register so little if any "stack" space is used to pass parameters. In fact basic types and pointers are both allocated on the stack. The stack size in .NET is limited to 1 MB. This should give you an idea of how few resources are consumed by parameter passing.

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

You may find this series of articles interesting:

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

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

通过堆栈分配提高性能(.NET内存管理:第2部分)

Which is faster? ByVal or ByRef.

哪个更快? ByVal或ByRef。

It is difficult at best to measure accurately and fairy - depending on the context of your measurement, but a benchmark I wrote calling a method 100 million times came up with the following:

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

  • Reference Type - Passed ByRef: 420 ms
  • 参考类型 - 通过ByRef:420毫秒

  • Reference Type - Passed ByVal: 382 ms
  • 参考类型 - 通过ByVal:382 ms

  • Value Type - Passed ByRef: 421 ms
  • 值类型 - 通过ByRef:421 ms

  • Value Type - Passed ByVal: 416 ms
  • 值类型 - 通过ByVal:416毫秒

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

Commenting out the variable and assignment in each method -

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

  • Reference Type - Passed ByRef: 389 ms
  • 参考类型 - 通过ByRef:389 ms

  • Reference Type - Passed ByVal: 349 ms
  • 参考类型 - 通过ByVal:349毫秒

  • Value Type - Passed ByRef: 416 ms
  • 值类型 - 通过ByRef:416毫秒

  • Value Type - Passed ByVal: 385 ms
  • 值类型 - 通过ByVal:385毫秒

One could conclude that passing reference types (strings, classes) ByVal will save some time. You might also say that passing value types (integer, byte) - ByVal will save some time.

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

Again the time is negligible in the grand scheme of things. What's more important is using ByVal and ByRef properly and understanding what's going on "behind the scenes." The algorithms implemented in your routines will most surely affect the runtime of your program many times more.

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

#2


If you're using a very large value type (Guid is pretty big, for example) it may be very slightly faster to pass a parameter by reference. In other cases, there may be more copying etc when you pass by reference than by value - for instance, if you've got a byte parameter, then one byte is clearly less than the four or eight bytes that the pointer would take if you passed it by reference.

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

In practice, you should almost never worry about this. Write the most readable code possible, which almost always means passing parameters by value instead of reference. I use ByRef very rarely.

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

If you want to improve performance and think that ByRef will help you, please benchmark it carefully (in your exact situation) before committing to it.

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

EDIT: I note in the comments to another (previously accepted, now deleted) answer that there's a great deal of misunderstanding about what ByRef vs ByVal means when it comes to value types. I have an article about parameter passing which has proved popular over the years - it's in C# terminology, but the same concepts apply to VB.NET.

编辑:我在评论中注意到另一个(以前接受的,现在删除的)答案,关于ByRef vs ByVal在价值类型方面的含义存在很多误解。我有一篇关于参数传递的文章,这篇文章多年来已经证明是流行的 - 它是在C#术语中,但同样的概念适用于VB.NET。

#3


It depends. If you are passing an object, it is already passing a pointer. That's why if you pass in an ArrayList (for instance) and your method adds somthing to the ArrayList, then the calling code also has the same object into it's ArrayList, that was passed in, because it's the same ArrayList. The only time that it doesn't pass a pointer, is when you pass a variable with an intrinsic data type, like an int, or a double, into the function. At that point, it creates a copy. However, the data size of these objects is so small, that it would hardly make a difference either way, in terms of memory usage or speed of execution.

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

#4


If you are passing in a reference type, ByRef is slower.

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

This is because what gets passed in is a pointer to a pointer. Any access to fields on the object requires dereferencing an extra pointer , which will take a few extra clock cycles to complete.

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

If you are passing a value type, then byref may be faster if the structure has many members, because it only passes a single pointer rather than copying the values on the stack. In terms of accessing members, byref will be slower because it needs to do an extra pointer dereference (sp->pValueType->member vs sp->member).

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

Most of the time in VB you shouldn't have to worry about this.

在VB中大多数时候你不必担心这一点。

In .NET it is rare to have value types with a large number of members. They are usually small. In that case, passing in a value type is no different than passing in multiple arguments to a procedure. For example, if you had code that passed in a Point object by value, it's perf would be the same as a method that took X and Y values as parameters. Seeing DoSomething(x as integer, y as integer) would probably not cause perf concerns. In fact, you would probably never think twice about it.

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

If you are defining large value types your self, then you should probably reconsider turning them into reference types.

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

The only other difference is the increase in the number of pointer indirections required to execute the code. It is rare that you ever need to optimize at that level. Most of the time, there are either algorithmic issues you can address, or your perf bottleneck is IO related, such as waiting for a database or writing to a file, in which case eliminating pointer indirections isn't going to help you much.

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

So, instead of focusing on wheter byval or byref is faster, I would recommend that you should really be focusing on what gives you the semantics that you need. In general, it's good idea to use byval unless you specifically need byref. It makes the program much easier to understand.

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

#5


While I don't know much about the internals of .NET, I'll discuss what I know about compiled languages. This does not apply to reference types, and may not be completely accurate about value types. If you don't know the difference between value types and reference types, you shouldn't read this. I'll assume 32-bit x86 (with 32-bit pointers).

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

  • Passing values smaller than 32-bits still uses a 32-bit object on the stack. Part of this object will be "unused" or "padding". Passing such values doesn't use less memory than passing 32-bit values.
  • 传递小于32位的值仍然在堆栈上使用32位对象。该对象的一部分将是“未使用”或“填充”。传递此类值不会比传递32位值使用更少的内存。

  • Passing values larger than 32-bits will use more stack space than a pointer, and probably more copying time.
  • 传递大于32位的值将使用比指针更多的堆栈空间,并且可能需要更多的复制时间。

  • If an object is passed by value, the callee can fetch the object from the stack. If an object is passed by reference, the callee must first fetch the object's address from the stack, and then fetch the object's value from elsewhere. By value means one less fetch, right? Well, actually the fetch needs to be done by the caller - however the caller may have already had to fetch for different reasons in which case a fetch is saved.
  • 如果按值传递对象,则被调用者可以从堆栈中获取对象。如果通过引用传递对象,则被调用者必须首先从堆栈中获取对象的地址,然后从其他位置获取对象的值。按价值意味着少拿一个,对吗?好吧,实际上fetch需要由调用者完成 - 但是调用者可能已经因为不同的原因而必须获取,在这种情况下保存了fetch。

  • Obviously any changes made to a by-reference value must be saved back to RAM, whereas a by-value parameter can be discarded.
  • 显然,对引用参考值所做的任何更改都必须保存回RAM,而可以丢弃按值参数。

  • It's better to pass by value, than to pass by reference only to copy the parameter into a local variable and not touch it again.
  • 传递值更好,而不是通过引用传递仅将参数复制到局部变量而不是再次触摸它。

The verdict:

It's much more important to understand what ByVal and ByRef actually do for you, and understand the difference between value and reference types, than to think about performance. The number one rule is to use whichever method is more appropriate to your code.

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

For large value types (more than 64 bits), pass by reference unless there is an advantage to passing by value (such as simpler code, "it just makes sense", or interface consistency).

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

For smaller value types, the passing mechanism doesn't make much difference to performance, and anyway it's hard to predict which method will be faster, since it depends on the object size, how the caller and callee use the object, and even cache considerations. Just do whatever makes sense for your code.

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

#6


ByVal creates a copy of the variable, whereas ByRef passes a pointer. I would therefore say that ByVal is slower (due to time it takes to copy) and uses more memory.

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

#7


My curiosity was to check the different behaviours depending object and memory usages

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

The result seems demostrate that ByVal always wins, the resource depends if collect memory or less (4.5.1 only)

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

#1


Byval and ByRef arguments should be used based on requirements and knowledge of how they work not on speed.

Byval和ByRef参数应该根据它们如何工作而不是速度的要求和知识来使用。

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

In response to a comment by Slough -

回应斯劳的评论 -

Which consumes more resources at runtime?

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

Parameters are passed on the stack. The stack is very fast, because its memory allocation is simply a pointer increment to reserve a new "frame" or "allocation record." Most .NET parameters do not exceed the size of a machine register so little if any "stack" space is used to pass parameters. In fact basic types and pointers are both allocated on the stack. The stack size in .NET is limited to 1 MB. This should give you an idea of how few resources are consumed by parameter passing.

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

You may find this series of articles interesting:

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

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

通过堆栈分配提高性能(.NET内存管理:第2部分)

Which is faster? ByVal or ByRef.

哪个更快? ByVal或ByRef。

It is difficult at best to measure accurately and fairy - depending on the context of your measurement, but a benchmark I wrote calling a method 100 million times came up with the following:

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

  • Reference Type - Passed ByRef: 420 ms
  • 参考类型 - 通过ByRef:420毫秒

  • Reference Type - Passed ByVal: 382 ms
  • 参考类型 - 通过ByVal:382 ms

  • Value Type - Passed ByRef: 421 ms
  • 值类型 - 通过ByRef:421 ms

  • Value Type - Passed ByVal: 416 ms
  • 值类型 - 通过ByVal:416毫秒

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

Commenting out the variable and assignment in each method -

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

  • Reference Type - Passed ByRef: 389 ms
  • 参考类型 - 通过ByRef:389 ms

  • Reference Type - Passed ByVal: 349 ms
  • 参考类型 - 通过ByVal:349毫秒

  • Value Type - Passed ByRef: 416 ms
  • 值类型 - 通过ByRef:416毫秒

  • Value Type - Passed ByVal: 385 ms
  • 值类型 - 通过ByVal:385毫秒

One could conclude that passing reference types (strings, classes) ByVal will save some time. You might also say that passing value types (integer, byte) - ByVal will save some time.

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

Again the time is negligible in the grand scheme of things. What's more important is using ByVal and ByRef properly and understanding what's going on "behind the scenes." The algorithms implemented in your routines will most surely affect the runtime of your program many times more.

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

#2


If you're using a very large value type (Guid is pretty big, for example) it may be very slightly faster to pass a parameter by reference. In other cases, there may be more copying etc when you pass by reference than by value - for instance, if you've got a byte parameter, then one byte is clearly less than the four or eight bytes that the pointer would take if you passed it by reference.

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

In practice, you should almost never worry about this. Write the most readable code possible, which almost always means passing parameters by value instead of reference. I use ByRef very rarely.

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

If you want to improve performance and think that ByRef will help you, please benchmark it carefully (in your exact situation) before committing to it.

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

EDIT: I note in the comments to another (previously accepted, now deleted) answer that there's a great deal of misunderstanding about what ByRef vs ByVal means when it comes to value types. I have an article about parameter passing which has proved popular over the years - it's in C# terminology, but the same concepts apply to VB.NET.

编辑:我在评论中注意到另一个(以前接受的,现在删除的)答案,关于ByRef vs ByVal在价值类型方面的含义存在很多误解。我有一篇关于参数传递的文章,这篇文章多年来已经证明是流行的 - 它是在C#术语中,但同样的概念适用于VB.NET。

#3


It depends. If you are passing an object, it is already passing a pointer. That's why if you pass in an ArrayList (for instance) and your method adds somthing to the ArrayList, then the calling code also has the same object into it's ArrayList, that was passed in, because it's the same ArrayList. The only time that it doesn't pass a pointer, is when you pass a variable with an intrinsic data type, like an int, or a double, into the function. At that point, it creates a copy. However, the data size of these objects is so small, that it would hardly make a difference either way, in terms of memory usage or speed of execution.

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

#4


If you are passing in a reference type, ByRef is slower.

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

This is because what gets passed in is a pointer to a pointer. Any access to fields on the object requires dereferencing an extra pointer , which will take a few extra clock cycles to complete.

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

If you are passing a value type, then byref may be faster if the structure has many members, because it only passes a single pointer rather than copying the values on the stack. In terms of accessing members, byref will be slower because it needs to do an extra pointer dereference (sp->pValueType->member vs sp->member).

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

Most of the time in VB you shouldn't have to worry about this.

在VB中大多数时候你不必担心这一点。

In .NET it is rare to have value types with a large number of members. They are usually small. In that case, passing in a value type is no different than passing in multiple arguments to a procedure. For example, if you had code that passed in a Point object by value, it's perf would be the same as a method that took X and Y values as parameters. Seeing DoSomething(x as integer, y as integer) would probably not cause perf concerns. In fact, you would probably never think twice about it.

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

If you are defining large value types your self, then you should probably reconsider turning them into reference types.

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

The only other difference is the increase in the number of pointer indirections required to execute the code. It is rare that you ever need to optimize at that level. Most of the time, there are either algorithmic issues you can address, or your perf bottleneck is IO related, such as waiting for a database or writing to a file, in which case eliminating pointer indirections isn't going to help you much.

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

So, instead of focusing on wheter byval or byref is faster, I would recommend that you should really be focusing on what gives you the semantics that you need. In general, it's good idea to use byval unless you specifically need byref. It makes the program much easier to understand.

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

#5


While I don't know much about the internals of .NET, I'll discuss what I know about compiled languages. This does not apply to reference types, and may not be completely accurate about value types. If you don't know the difference between value types and reference types, you shouldn't read this. I'll assume 32-bit x86 (with 32-bit pointers).

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

  • Passing values smaller than 32-bits still uses a 32-bit object on the stack. Part of this object will be "unused" or "padding". Passing such values doesn't use less memory than passing 32-bit values.
  • 传递小于32位的值仍然在堆栈上使用32位对象。该对象的一部分将是“未使用”或“填充”。传递此类值不会比传递32位值使用更少的内存。

  • Passing values larger than 32-bits will use more stack space than a pointer, and probably more copying time.
  • 传递大于32位的值将使用比指针更多的堆栈空间,并且可能需要更多的复制时间。

  • If an object is passed by value, the callee can fetch the object from the stack. If an object is passed by reference, the callee must first fetch the object's address from the stack, and then fetch the object's value from elsewhere. By value means one less fetch, right? Well, actually the fetch needs to be done by the caller - however the caller may have already had to fetch for different reasons in which case a fetch is saved.
  • 如果按值传递对象,则被调用者可以从堆栈中获取对象。如果通过引用传递对象,则被调用者必须首先从堆栈中获取对象的地址,然后从其他位置获取对象的值。按价值意味着少拿一个,对吗?好吧,实际上fetch需要由调用者完成 - 但是调用者可能已经因为不同的原因而必须获取,在这种情况下保存了fetch。

  • Obviously any changes made to a by-reference value must be saved back to RAM, whereas a by-value parameter can be discarded.
  • 显然,对引用参考值所做的任何更改都必须保存回RAM,而可以丢弃按值参数。

  • It's better to pass by value, than to pass by reference only to copy the parameter into a local variable and not touch it again.
  • 传递值更好,而不是通过引用传递仅将参数复制到局部变量而不是再次触摸它。

The verdict:

It's much more important to understand what ByVal and ByRef actually do for you, and understand the difference between value and reference types, than to think about performance. The number one rule is to use whichever method is more appropriate to your code.

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

For large value types (more than 64 bits), pass by reference unless there is an advantage to passing by value (such as simpler code, "it just makes sense", or interface consistency).

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

For smaller value types, the passing mechanism doesn't make much difference to performance, and anyway it's hard to predict which method will be faster, since it depends on the object size, how the caller and callee use the object, and even cache considerations. Just do whatever makes sense for your code.

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

#6


ByVal creates a copy of the variable, whereas ByRef passes a pointer. I would therefore say that ByVal is slower (due to time it takes to copy) and uses more memory.

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

#7


My curiosity was to check the different behaviours depending object and memory usages

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

The result seems demostrate that ByVal always wins, the resource depends if collect memory or less (4.5.1 only)

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