vb.net performance byref byval

vb.net - ¿Cual es mas rápido? ByVal o ByRef?



performance (7)

Depende. Si está pasando un objeto, ya está pasando un puntero. Es por eso que si transfiere un ArrayList (por ejemplo) y su método agrega algo a ArrayList, entonces el código de llamada también tiene el mismo objeto en su ArrayList, que se transfirió, porque es el mismo ArrayList. El único momento en que no pasa un puntero es cuando pasa una variable con un tipo de datos intrínseco, como un int, o un doble, a la función. En ese punto, crea una copia. Sin embargo, el tamaño de los datos de estos objetos es tan pequeño que difícilmente haría una diferencia en ambos sentidos, en términos de uso de memoria o velocidad de ejecución.

En VB.NET, que es más rápido de usar para los argumentos del método, ByVal o ByRef ?

Además, ¿qué consume más recursos en tiempo de ejecución (RAM)?

Leí esta pregunta , pero las respuestas no son aplicables o lo suficientemente específicas.


Los argumentos de Byval y ByRef deben usarse según los requisitos y el conocimiento de cómo funcionan, no de la velocidad.

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

En respuesta a un comentario de Slough -

¿Qué consume más recursos en tiempo de ejecución?

Los parámetros se pasan en la pila. La pila es muy rápida, porque su asignación de memoria es simplemente un incremento de puntero para reservar un nuevo "marco" o "registro de asignación". La mayoría de los parámetros de .NET no exceden el tamaño de un registro de máquina tan poco si se usa un espacio de "pila" para pasar los parámetros. De hecho, los tipos básicos y punteros están asignados en la pila. El tamaño de pila en .NET está limitado a 1 MB. Esto debería darle una idea de qué tan pocos recursos se consumen mediante el paso de parámetros.

Puede encontrar interesante esta serie de artículos:

Mejora del rendimiento a través de la asignación de pila (.NET Memory Management: Parte 2)

¿Cual es mas rápido? ByVal o ByRef.

En el mejor de los casos, es difícil medir con precisión y hadas, dependiendo del contexto de su medida, pero un punto de referencia que escribí llamando a un método 100 millones de veces surgió con lo siguiente:

  • Tipo de referencia - Pasado ByRef: 420 ms
  • Tipo de referencia - Pasado por Val: 382 ms
  • Tipo de valor: pasado por Ref: 421 ms
  • Tipo de valor - Pasado por Val: 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

Comentando la variable y la asignación en cada método -

  • Tipo de referencia - Pasado ByRef: 389 ms
  • Tipo de referencia - Pasado por Val: 349 ms
  • Tipo de valor - Pasado ByRef: 416 ms
  • Tipo de valor - Pasado por Val: 385 ms

Se podría concluir que pasar tipos de referencia (cadenas, clases) ByVal ahorrará tiempo. También podría decir que pasar tipos de valores (entero, byte) - ByVal ahorrará tiempo.

De nuevo, el tiempo es insignificante en el gran esquema de cosas. Lo que es más importante es usar ByVal y ByRef correctamente y comprender lo que está sucediendo "detrás de escena". Los algoritmos implementados en sus rutinas seguramente afectarán el tiempo de ejecución de su programa muchas veces más.


Mi curiosidad era comprobar los diferentes comportamientos según los usos de memoria y objeto

El resultado parece demostrar que ByVal siempre gana, el recurso depende si recolectar memoria o menos (solo 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


Si bien no sé mucho sobre los aspectos internos de .NET, analizaré lo que sé sobre los lenguajes compilados. Esto no se aplica a los tipos de referencia , y puede no ser completamente preciso sobre los tipos de valores. Si no conoce la diferencia entre los tipos de valores y los tipos de referencia, no debe leer esto. Asumiré 32-bit x86 (con punteros de 32 bits).

  • Al pasar valores menores que 32 bits todavía se usa un objeto de 32 bits en la pila. Parte de este objeto estará "sin usar" o "relleno". Pasar tales valores no usa menos memoria que pasar valores de 32 bits.
  • Al pasar valores de más de 32 bits se usará más espacio de pila que un puntero, y probablemente más tiempo de copia.
  • Si un objeto se pasa por valor, el destinatario puede recuperar el objeto de la pila. Si un objeto se pasa por referencia, el destinatario primero debe recuperar la dirección del objeto de la pila y luego recuperar el valor del objeto de otra parte. Por valor significa una búsqueda menos, ¿verdad? Bueno, en realidad la búsqueda debe ser realizada por la persona que llama; sin embargo, es posible que la persona que llama ya haya tenido que buscarla por diferentes motivos, en cuyo caso se guarda una búsqueda.
  • Obviamente, cualquier cambio realizado en un valor de referencia por referencia se debe guardar en la RAM, mientras que un parámetro de valor por defecto se puede descartar.
  • Es mejor pasar por valor, que pasar por referencia solo para copiar el parámetro en una variable local y no volver a tocarlo.

El veredicto:

Es mucho más importante entender qué ByVal y ByRef realmente hacen por usted, y comprender la diferencia entre el valor y los tipos de referencia, que pensar en el rendimiento. La regla número uno es usar el método que sea más apropiado para su código .

Para tipos de valores grandes (más de 64 bits), pase por referencia a menos que haya una ventaja para pasar por el valor (como un código más simple, "simplemente tiene sentido" o consistencia de la interfaz).

Para tipos de valor más pequeños, el mecanismo de pase no hace mucha diferencia en el rendimiento, y de todos modos es difícil predecir qué método será más rápido, ya que depende del tamaño del objeto, cómo la persona que llama y quien llama utiliza el objeto e incluso consideraciones de caché . Simplemente haz lo que tenga sentido para tu código.


Si está pasando un tipo de referencia, ByRef es más lento.

Esto se debe a que lo que se transfiere es un puntero a un puntero. Cualquier acceso a los campos en el objeto requiere desreferenciar un puntero adicional, lo que requerirá unos ciclos de reloj adicionales para completarse.

Si está pasando un tipo de valor, entonces byref puede ser más rápido si la estructura tiene muchos miembros, porque solo pasa un solo puntero en lugar de copiar los valores en la pila. En términos de acceso a miembros, byref será más lento porque necesita hacer una desreferencia de puntero adicional (sp-> pValueType-> member vs sp-> member).

La mayoría de las veces en VB no deberías preocuparte por esto.

En .NET es raro tener tipos de valores con una gran cantidad de miembros. Por lo general son pequeños. En ese caso, pasar un tipo de valor no es diferente a pasar múltiples argumentos a un procedimiento. Por ejemplo, si tiene un código que pasó en un objeto Point por valor, su rendimiento sería el mismo que el que tomó los valores X e Y como parámetros. Ver DoSomething (x como entero, y como entero) probablemente no cause problemas de rendimiento. De hecho, probablemente nunca lo pienses dos veces.

Si está definiendo usted mismo tipos de valores grandes, entonces probablemente debería reconsiderar convertirlos en tipos de referencia.

La única otra diferencia es el aumento en el número de indirectas del puntero requeridas para ejecutar el código. Es raro que alguna vez necesite optimizar a ese nivel. La mayoría de las veces, hay problemas algorítmicos que puede resolver, o su cuello de botella está relacionado con IO, como esperar una base de datos o escribir en un archivo, en cuyo caso eliminar las indirecciones del puntero no lo ayudará demasiado.

Por lo tanto, en lugar de centrarme en si el byval o el byref son más rápidos, recomendaría que realmente se concentre en lo que le da la semántica que necesita. En general, es una buena idea usar byval a menos que específicamente lo necesite por refrendo. Hace que el programa sea mucho más fácil de entender.


Si está utilizando un tipo de valor muy grande (Guid es bastante grande, por ejemplo), puede ser un poco más rápido pasar un parámetro por referencia. En otros casos, puede haber más copia, etc. cuando pasa por referencia que por valor; por ejemplo, si tiene un parámetro de byte, entonces un byte es claramente menor que los cuatro u ocho bytes que tomaría el puntero si lo pasó por referencia.

En la práctica, casi nunca deberías preocuparte por esto. Escriba el código más legible posible, que casi siempre significa pasar los parámetros por valor en lugar de referencia. Uso ByRef muy raramente.

Si desea mejorar el rendimiento y piensa que ByRef lo ayudará, haga una evaluación comparativa (en su situación exacta) antes de comprometerse.

EDITAR: Noté en los comentarios a otra respuesta (previamente aceptada, ahora eliminada) que hay una gran cantidad de malentendidos sobre lo que ByRef vs ByVal significa cuando se trata de tipos de valores. Tengo un artículo sobre el paso de parámetros que ha demostrado ser popular a lo largo de los años: está en la terminología de C #, pero los mismos conceptos se aplican a VB.NET.


ByVal crea una copia de la variable, mientras que ByRef pasa un puntero. Por lo tanto, diría que ByVal es más lento (debido al tiempo que lleva copiarlo) y usa más memoria.