Pergunta

Em VB.NET, que é mais rápido para o uso de argumentos de método, ByVal ou ByRef?

Além disso, o que consome mais recursos em tempo de execução (RAM)?


Eu li através esta questão , mas as respostas não são suficiente aplicável ou específico.

Foi útil?

Solução

argumentos

ByVal e ByRef devem ser usados ??com base nos requisitos e conhecimentos de como eles funcionam não na velocidade.

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

Em resposta a um comentário de Slough -

O que consome mais recursos em tempo de execução?

Os parâmetros são passados ??na pilha. A pilha é muito rápido, porque a sua alocação de memória é simplesmente um incremento de ponteiro para reservar um novo "frame" ou "registro de alocação." A maioria dos parâmetros NET não excedam o tamanho de uma máquina de registo de modo pouco ou nenhum espaço "pilha" é usado para passar parâmetros. Na verdade tipos básicos e ponteiros são ambos alocada na pilha. O tamanho da pilha em .NET é limitado a 1 MB. Isso deve lhe dar uma idéia de como os recursos alguns são consumidos por passagem de parâmetro.

Você pode encontrar esta série de artigos interessantes:

Melhorar o desempenho Através Stack Allocation (Gestão .NET Memória: Part 2)

O que é mais rápido? ByVal ou ByRef.

É difícil no melhor para medir com precisão e fadas - dependendo do contexto da sua medida, mas uma referência eu escrevi chamar um método 100 milhões de vezes veio com o seguinte:

  • Tipo de Referência - Passou ByRef: 420 ms
  • Tipo de Referência - Passou ByVal: 382 ms
  • Valor Tipo - Passou ByRef: 421 ms
  • Valor Tipo - Passou 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

Comentando a variável e atribuição em cada método -

  • Tipo de Referência - Passou ByRef: 389 ms
  • Tipo de Referência - Passou ByVal: 349 ms
  • Valor Tipo - Passou ByRef: 416 ms
  • Valor Tipo - Passou ByVal: 385 ms

Pode-se concluir que a passagem de referência tipos (cordas, classes) ByVal irá poupar algum tempo. Você também pode dizer que passando tipos de valores (inteiros, byte) -. ByVal irá poupar algum tempo

Mais uma vez o tempo é insignificante no grande esquema das coisas. O que é mais importante é usar ByVal e ByRef corretamente e entender o que está acontecendo "nos bastidores". Os algoritmos implementados em suas rotinas mais certamente afetar o tempo de execução do seu programa muitas vezes mais.

Outras dicas

Se você estiver usando um grande tipo de valor (Guid é muito grande, por exemplo) pode ser muito ligeiramente mais rápido para passar um parâmetro por referência. Em outros casos, pode haver mais copiar etc quando você passar por referência de por valor - por exemplo, se você tem um parâmetro byte, então um byte é claramente menos do que os quatro ou oito bytes que o ponteiro levaria se você passou por referência.

Na prática, você quase nunca deve se preocupar com isso. Escrever o mais legível código possível, o que quase sempre significa passar parâmetros por valor em vez de referência. Eu uso ByRef muito raramente.

Se você quiser melhorar o desempenho e acho que ByRef irá ajudá-lo, por favor referência-lo com cuidado (em sua situação exata) antes de submetê-lo.

EDIT: Nota I nos comentários a outra (previamente aceites, agora suprimido) resposta que há um grande mal-entendido sobre o que ByRef vs meios ByVal quando se trata de tipos de valor. Eu tenho um artigo sobre passagem de parâmetros que provou ser popular ao longo dos anos - está em C # terminologia, mas os mesmos conceitos se aplicam a VB.NET.

Depende. Se você estiver passando um objeto, ele já está passando um ponteiro. É por isso que se você passar em um ArrayList (por exemplo) e seu método adiciona somthing para o ArrayList, em seguida, o código de chamada também tem o mesmo objeto em que o ArrayList, que foi aprovada em, porque é o mesmo ArrayList. A única vez que não passa um ponteiro, é quando você passar uma variável com um tipo de dados intrínseco, como um int, ou um duplo, para a função. Nesse ponto, ele cria uma cópia. No entanto, o tamanho dos dados desses objetos é tão pequena, que dificilmente fazer uma diferença de qualquer forma, em termos de uso de memória ou velocidade de execução.

Se você está passando em um tipo de referência, ByRef é mais lento.

Isto porque o que se passou na é um ponteiro para um ponteiro. Qualquer acesso a campos no objeto requer dereferencing um ponteiro extra, o que vai demorar alguns ciclos de clock extra para completar.

Se você estiver passando um tipo de valor, então byref pode ser mais rápido se a estrutura tem muitos membros, porque ele só passa um único ponteiro ao invés de copiar os valores na pilha. Em termos de membros acessam, byref será mais lento porque ele precisa fazer um dereference ponteiro adicional (SP-> pValueType-> membro vs SP-> membro).

Na maioria das vezes em VB você não deve ter que se preocupar com isso.

Em .NET é raro ter tipos de valor com um grande número de membros. Eles são geralmente pequenas. Nesse caso, passando em um tipo de valor não é diferente do que passar em vários argumentos para um procedimento. Por exemplo, se você tivesse um código que passou em um objeto Point por valor, é perf seria o mesmo que um método que levou valores X e Y como parâmetros. Vendo DoSomething (x um número inteiro, y um número inteiro) provavelmente não iria causar preocupações perf. Na verdade, você provavelmente nunca pensar duas vezes sobre isso.

Se você estiver definindo grandes tipos de valor a si mesmo, então você provavelmente deve reconsiderar transformando-os em tipos de referência.

A única diferença é o aumento no número de indireções ponteiro necessários para executar o código. É raro que você nunca precisa para otimizar a esse nível. Na maioria das vezes, há questões quer algorítmicos você pode resolver, ou o seu gargalo perf é IO relacionados, tais como a espera de um banco de dados ou escrever para um arquivo, caso em que a eliminação indireções ponteiro não vai ajudar muito.

Assim, em vez de se concentrar em wheter byval ou byref é mais rápido, eu recomendo que você realmente deve ser se concentrar no que você dá a semântica que você precisa. Em geral, é boa idéia usar byval a menos que você especificamente precisa byref. Isso torna o programa muito mais fácil de entender.

Enquanto eu não sei muito sobre os internos do .NET, vou discutir o que eu sei línguas sobre compilados. Este não se aplica aos tipos de referência , e pode não ser completamente precisos sobre os tipos de valor. Se você não sabe a diferença entre os tipos de valor e tipos de referência, você não deve ler este. Eu vou assumir x86 de 32 bits (com ponteiros de 32 bits).

  • Passar valores menores que 32 bits ainda usa um objeto de 32 bits na pilha. Parte deste objeto será "não utilizado" ou "padding". Passando tais valores não usa menos memória do que passar valores de 32 bits.
  • valores Passando maiores que 32 bits vai usar mais espaço de pilha do que um ponteiro, e provavelmente mais copiando tempo.
  • Se um objeto é passado por valor, o receptor pode buscar o objeto da pilha. Se um objeto é passado por referência, o receptor deve primeiro buscar o endereço do objeto da pilha, e depois buscar o valor do objeto de outro lugar. Por meio de valor um a menos buscar, certo? Bem, na verdade o que precisa ser feito pelo chamador buscar -. No entanto, o chamador pode ter já teve para buscar por diferentes razões, caso em que uma busca é salvo
  • Obviamente quaisquer alterações feitas em um valor por referência deve ser salvo de volta à RAM, enquanto que um by-valor do parâmetro pode ser descartado.
  • É melhor para passar por valor, do que passar por referência apenas para copiar o parâmetro em uma variável local e não tocá-la novamente.

O veredicto:

É muito mais importante entender o que ByVal e ByRef realmente fazer por você, e compreender a diferença entre os tipos de valor e de referência, do que pensar sobre o desempenho. A regra número um é Use o método é mais apropriado ao seu código .

Para tipos de valores grandes (mais do que 64 bits), passar por referência a menos que haja uma vantagem para passar por valor (tais como códigos mais simples, "apenas faz sentido", ou consistência da interface).

Para tipos de valores menores, o mecanismo de passagem não faz muita diferença no desempenho, e mesmo assim é difícil prever qual método será mais rápido, uma vez que depende do tamanho do objeto, como o chamador e chamado usar o objeto, e mesmo armazenar em cache considerações. Basta fazer o que faz sentido para o seu código.

ByVal cria uma cópia da variável, enquanto que ByRef passa um ponteiro. Assim, gostaria de dizer que ByVal é mais lento (devido ao tempo que leva para copiar) e usa mais memória.

A minha curiosidade foi verificar os diferentes comportamentos, dependendo do objeto e de memória usos

O resultado parece demostrate que ByVal sempre vence, o recurso depende se a memória coleta ou menos (4.5.1 apenas)

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top