问题
In VB.NET, which is faster to use for method arguments, ByVal
or ByRef
?
Also, which consumes more resources at runtime (RAM)?
I read through this question, but the answers are not applicable or specific enough.
回答1:
Byval and ByRef arguments should be used based on requirements and knowledge of how they work not on speed.
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.
You may find this series of articles interesting:
Improving Performance Through Stack Allocation (.NET Memory Management: Part 2)
Which is faster? ByVal or 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:
- Reference Type - Passed ByRef: 420 ms
- Reference Type - Passed ByVal: 382 ms
- Value Type - Passed ByRef: 421 ms
- Value Type - Passed 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
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
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.
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.
回答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.
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.
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.
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.
回答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.
回答4:
If you are passing in a reference type, ByRef is slower.
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).
Most of the time in VB you shouldn't have to worry about this.
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.
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.
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.
回答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).
- 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.
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.
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).
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.
回答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)
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
来源:https://stackoverflow.com/questions/408101/which-is-faster-byval-or-byref