vb.net 哪个更快?ByVal 还是 ByRef?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/408101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 14:00:29  来源:igfitidea点击:

Which is faster? ByVal or ByRef?

vb.netperformancebyrefbyval

提问by TheFlash

In VB.NET, which is faster to use for method arguments, ByValor 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.

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

回答by user50612

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

Byval 和 ByRef 参数的使用应该基于需求和它们如何工作的知识,而不是速度。

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

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
  • Reference Type - Passed ByVal: 382 ms
  • Value Type - Passed ByRef: 421 ms
  • Value Type - Passed ByVal: 416 ms
  • 引用类型 - 传递 ByRef:420 毫秒
  • 参考类型 - 传递 ByVal:382 毫秒
  • 值类型 - 传递 ByRef:421 毫秒
  • 值类型 - 传递 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
  • Reference Type - Passed ByVal: 349 ms
  • Value Type - Passed ByRef: 416 ms
  • Value Type - Passed ByVal: 385 ms
  • 引用类型 - 传递 ByRef:389 毫秒
  • 参考类型 - 传递 ByVal:349 毫秒
  • 值类型 - 传递 ByRef:416 毫秒
  • 值类型 - Passed 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 并了解“幕后”发生了什么。在你的例程中实现的算法肯定会多次影响你的程序的运行时间。

回答by Jon Skeet

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 morecopying 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 readablecode 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, pleasebenchmark 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 passingwhich has proved popular over the years - it's in C# terminology, but the same concepts apply to VB.NET.

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

回答by Kibbee

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)传递到函数中。此时,它会创建一个副本。但是,这些对象的数据大小非常小,因此无论哪种方式,无论是内存使用量还是执行速度,都不会产生影响。

回答by Scott Wisniewski

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 对象的代码,它的性能将与将 X 和 Y 值作为参数的方法相同。看到 DoSomething(x 作为整数,y 作为整数) 可能不会引起性能问题。事实上,你可能永远不会三思而后行。

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.

因此,与其关注 byval 还是 byref 更快,我建议您应该真正关注为您提供所需语义的东西。通常,除非您特别需要 byref,否则最好使用 byval。它使程序更容易理解。

回答by Artelius

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.
  • Passing values larger than 32-bits will use more stack space than a pointer, and probably more copying time.
  • 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.
  • Obviously any changes made to a by-reference value must be saved back to RAM, whereas a by-value parameter can be discarded.
  • 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.
  • 传递小于 32 位的值仍然使用堆栈上的 32 位对象。此对象的一部分将“未使用”或“填充”。传递这些值不会比传递 32 位值使用更少的内存。
  • 传递大于 32 位的值将使用比指针更多的堆栈空间,并且可能更多的复制时间。
  • 如果对象是按值传递的,则被调用者可以从堆栈中获取该对象。如果对象是通过引用传递的,则被调用者必须首先从堆栈中获取对象的地址,然后从其他地方获取对象的值。价值意味着少一个获取,对吧?好吧,实际上提取需要由调用者完成——但是调用者可能已经因为不同的原因不得不提取,在这种情况下提取被保存。
  • 显然,对按引用值所做的任何更改都必须保存回 RAM,而按值参数可以丢弃。
  • 最好是按值传递,而不是按引用传递只是为了将参数复制到局部变量中而不再次接触它。

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.

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

回答by Colin

ByValcreates a copy of the variable, whereas ByRefpasses a pointer. I would therefore say that ByValis slower (due to time it takes to copy) and uses more memory.

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

回答by Fabio Guerrazzi

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