Pregunta

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

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


Leí esta pregunta , pero las respuestas no son aplicable o lo suficientemente específico.

¿Fue útil?

Solución

Los argumentos de

Byval y ByRef se deben usar en función de los requisitos y el conocimiento de cómo funcionan no en 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 o ninguno " pila " El espacio se utiliza para pasar parámetros. De hecho, los tipos básicos y los punteros están asignados en la pila. El tamaño de pila en .NET está limitado a 1 & nbsp; MB. Esto debería darle una idea de la cantidad de recursos que consume el paso de parámetros.

Puede que encuentres esta serie de artículos interesantes:

Mejora del rendimiento mediante la asignación de pila (.NET Memory Management: Part 2)

¿Cuál es más rápido? ByVal o ByRef.

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

  • Tipo de referencia: ByRef aprobado: 420 ms
  • Tipo de referencia - Aprobado a partir de: 382 ms
  • Tipo de valor: ByRef aprobado: 421 ms
  • Tipo de valor - Pasado por el valor: 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: ByRef aprobado: 389 ms
  • Tipo de referencia - Aprobado a partir de: 349 ms
  • Tipo de valor: ByRef aprobado: 416 ms
  • Tipo de valor - Pasado por el valor: 385 ms

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

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

Otros consejos

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 copiando, 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 pasara por referencia.

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

Si desea mejorar el rendimiento y cree que ByRef lo ayudará, por favor realice una evaluación comparativa (en su situación exacta) antes de comprometerse con él.

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

Depende. Si está pasando un objeto, ya está pasando un puntero. Es por eso que si pasa 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 pasó, porque es el mismo ArrayList. La única vez que no pasa un puntero, es cuando se 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 supondría una diferencia en términos de uso de memoria o velocidad de ejecución.

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

Esto se debe a que lo que se pasa es un puntero a un puntero. Cualquier acceso a los campos en el objeto requiere eliminar la referencia a un puntero adicional, que llevará unos pocos ciclos de reloj adicionales para completar.

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ía preocuparse por esto.

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

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

La única otra diferencia es el aumento en el número de direccionamientos de puntero necesarios para ejecutar el código. Es raro que alguna vez necesites optimizar a ese nivel. La mayoría de las veces, hay problemas algorítmicos que puede solucionar, o su cuello de botella de perf se relaciona con la IO, como esperar a una base de datos o escribir en un archivo, en cuyo caso, eliminar los desvíos de punteros no será de gran ayuda.

Por lo tanto, en lugar de centrarse en que byby o byref sea más rápido, recomendaría que realmente debería centrarse en lo que le proporciona la semántica que necesita. En general, es una buena idea usar byval a menos que específicamente necesite byref. Hace que el programa sea mucho más fácil de entender.

Aunque no sé mucho sobre los aspectos internos de .NET, discutiré lo que sé sobre los idiomas compilados. Este no se aplica a los tipos de referencia , y puede que no sea completamente exacto sobre los tipos de valor. Si no conoce la diferencia entre los tipos de valor y los tipos de referencia, no debe leer esto. Asumiré x86 de 32 bits (con punteros de 32 bits).

  • Pasar valores menores a 32 bits aún utiliza un objeto de 32 bits en la pila. Parte de este objeto será " no utilizado " o " relleno " ;. Pasar tales valores no usa menos memoria que pasar valores de 32 bits.
  • Pasar valores mayores a 32 bits 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 de la llamada puede recuperar el objeto de la pila. Si se pasa un objeto por referencia, el destinatario de la llamada debe primero obtener la dirección del objeto de la pila y luego obtener el valor del objeto de otra parte. Por valor significa una búsqueda menos, ¿verdad? Bueno, en realidad, la persona que llama debe realizar la búsqueda, 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 debe guardarse en la RAM, mientras que un parámetro de valor puede ser descartado.
  • 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 lo que ByVal y ByRef realmente hacen por usted, y entender la diferencia entre los tipos de valor y referencia, que pensar en el rendimiento. La regla número uno es utilizar el método que sea más apropiado para su código .

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

Para tipos de valor más pequeños, el mecanismo de paso 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, de cómo el llamante y el que llama utilizan el objeto, y incluso las consideraciones de caché. Solo haz lo que tenga sentido para tu código.

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 se tarda en copiar) y usa más memoria.

Mi curiosidad era comprobar los diferentes comportamientos según el uso de objetos y memoria

El resultado parece demostrar que ByVal siempre gana, el recurso depende de si recopilamos 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top