Question

Dans VB.NET, qui est plus rapide à utiliser pour les arguments de méthode, ByVal ou ByRef ?

Aussi, qui consomme plus de ressources au moment de l'exécution (RAM)?

J'ai lu cette question , mais les réponses ne sont pas. applicable ou assez spécifique.

Était-ce utile?

La solution

Les arguments Byval et ByRef doivent être utilisés en fonction des besoins et de la connaissance dont ils fonctionnent et non pas en termes de vitesse.

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

En réponse à un commentaire de Slough -

Qui consomme plus de ressources à l'exécution?

Les paramètres sont passés sur la pile. La pile est très rapide, car son allocation de mémoire est simplement un incrément de pointeur pour réserver un nouveau "frame". ou " enregistrement d'allocation. " La plupart des paramètres .NET ne dépassent pas la taille d’un registre d’ordinateur si peu ou pas du tout "pile". l'espace est utilisé pour passer des paramètres. En fait, les types de base et les pointeurs sont tous deux alloués sur la pile. La taille de la pile dans .NET est limitée à 1 MB. Cela devrait vous donner une idée du peu de ressources consommées par le passage de paramètres.

Vous pouvez trouver cette série d'articles intéressants:

Amélioration des performances grâce à l'allocation de pile (Gestion de la mémoire .NET: Part 2)

Lequel est le plus rapide? ByVal ou ByRef.

Au mieux, il est difficile de mesurer avec précision et exactitude - en fonction du contexte de votre mesure, mais j’ai écrit un point de référence appelant une méthode 100 millions de fois:

  • Type de référence - Transmis par la référence: 420 ms
  • Type de référence - Validé avec succès: 382 ms
  • Type de valeur - Transmis par référence: 421 ms
  • Type de valeur - Validation passée: 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

Commenter la variable et l'affectation dans chaque méthode -

  • Type de référence - Transmis par la référence: 389 ms
  • Type de référence - Validé avec succès: 349 ms
  • Type de valeur - Transmis par référence: 416 ms
  • Type de valeur - Validé avec succès: 385 ms

On pourrait en conclure que passer des types de référence (chaînes, classes) à ByVal vous fera gagner du temps. Vous pourriez également dire que le fait de passer des types de valeur (entier, octet) - ByVal vous fera gagner du temps.

Encore une fois, le temps est négligeable dans le grand schéma des choses. Le plus important est d’utiliser correctement ByVal et ByRef et de comprendre ce qui se passe "dans les coulisses". Les algorithmes mis en œuvre dans vos routines affecteront certainement beaucoup plus l'exécution du programme.

Autres conseils

Si vous utilisez un type de valeur très grand (Guid est assez gros, par exemple), il peut être très légèrement plus rapide de passer un paramètre par référence. Dans d'autres cas, il peut y avoir plus de copies, etc. lorsque vous passez par référence que par valeur. Par exemple, si vous avez un paramètre d'octet, un octet est clairement inférieur à quatre ou huit octets. que le pointeur prendrait si vous le passiez par référence.

En pratique, vous ne devriez presque jamais vous inquiéter de cela. Ecrivez le code le plus lisible possible, ce qui signifie presque toujours que vous transmettez des paramètres par valeur plutôt que par référence. J'utilise très rarement ByRef.

Si vous souhaitez améliorer les performances et pensez que ByRef vous aidera, veuillez le comparer avec soin (dans votre situation exacte) avant de vous y engager.

EDIT: Je remarque dans les commentaires d'une autre réponse (précédemment acceptée, maintenant supprimée) qu'il existe de nombreux malentendus sur ce que ByRef vs ByVal signifie en ce qui concerne les types de valeur. J'ai un article sur le passage de paramètres qui s'est révélé populaire au fil des ans - il est en C #. terminologie, mais les mêmes concepts s’appliquent à VB.NET.

Cela dépend. Si vous passez un objet, il passe déjà un pointeur. C'est pourquoi, si vous transmettez une ArrayList (par exemple) et que votre méthode ajoute quelque chose à ArrayList, alors le code appelant a également le même objet dans son ArrayList, qui a été passé, parce que c'est le même ArrayList. La seule fois où il ne passe pas un pointeur, c'est lorsque vous passez une variable avec un type de données intrinsèque, comme un int ou un double, dans la fonction. À ce stade, il crée une copie. Toutefois, la taille des données de ces objets est si petite que cela ne ferait guère de différence, que ce soit en termes d’utilisation de la mémoire ou de vitesse d’exécution.

Si vous passez un type de référence, ByRef est plus lent.

Cela est dû au fait que ce qui est transmis est un pointeur sur un pointeur. Tout accès aux champs de l'objet nécessite de déréférencer un pointeur supplémentaire, ce qui prend quelques cycles d'horloge supplémentaires.

Si vous transmettez un type de valeur, alors byref peut être plus rapide si la structure compte plusieurs membres, car elle ne transmet qu'un seul pointeur au lieu de copier les valeurs de la pile. En ce qui concerne l'accès aux membres, byref sera plus lent, car il doit effectuer une déréférence de pointeur supplémentaire (sp- > pValueType- & member) à membre -sp- > membre).

La plupart du temps en VB, vous ne devriez pas avoir à vous soucier de cela.

Dans .NET, il est rare d'avoir des types de valeur avec un grand nombre de membres. Ils sont généralement petits. Dans ce cas, la transmission d'un type de valeur n'est pas différente de la transmission de plusieurs arguments à une procédure. Par exemple, si vous aviez un code qui passait dans un objet Point par valeur, sa performance serait identique à une méthode qui prend les valeurs X et Y en tant que paramètres. Voir Quelque chose (x en entier, y en entier) ne poserait probablement pas de problème de performance. En fait, vous n'y penserez probablement probablement jamais plus.

Si vous définissez vous-même des types de grande valeur, vous devriez probablement reconsidérer leur conversion en types de référence.

La seule autre différence est l’augmentation du nombre d’indexions de pointeur requises pour exécuter le code. Il est rare que vous ayez besoin d'optimiser à ce niveau. La plupart du temps, vous pouvez résoudre des problèmes d’algorithme ou votre goulet d’étranglement est lié aux entrées-sorties, comme attendre une base de données ou écrire dans un fichier. Dans ce cas, éliminer les indirections de pointeur ne vous aidera pas beaucoup. / p>

Donc, au lieu de vous concentrer sur le fait que byval ou byref soit plus rapide, je vous recommande de vous concentrer sur ce qui vous donne la sémantique dont vous avez besoin. En général, c'est une bonne idée d'utiliser byval sauf si vous avez spécifiquement besoin de byref. Cela rend le programme beaucoup plus facile à comprendre.

Bien que je ne connaisse pas grand chose au sujet des composants internes de .NET, je vais parler de ce que je sais des langages compilés. Ce ne s’applique pas aux types de référence et peut ne pas être parfaitement précis en ce qui concerne les types de valeur. Si vous ne connaissez pas la différence entre les types de valeur et les types de référence, vous ne devriez pas lire ceci. Je supposerai x86 32 bits (avec des pointeurs 32 bits).

  • Le passage de valeurs inférieures à 32 bits utilise toujours un objet 32 ??bits sur la pile. Une partie de cet objet sera "inutilisée". ou "rembourrage". Passer de telles valeurs n'utilise pas moins de mémoire que de transmettre des valeurs 32 bits.
  • Les valeurs supérieures à 32 bits utiliseront plus d'espace de pile qu'un pointeur et probablement plus de temps de copie.
  • Si un objet est passé par valeur, l'appelé peut extraire l'objet de la pile. Si un objet est passé par référence, l'appelé doit d'abord extraire l'adresse de l'objet de la pile, puis extraire la valeur de l'objet ailleurs. Par valeur signifie une extraction de moins, non? L’appelant doit en fait effectuer la récupération. Toutefois, il se peut que celui-ci ait déjà eu à extraire du contenu pour différentes raisons. Dans ce cas, une récupération est enregistrée.
  • Évidemment, toute modification apportée à une valeur de référence doit être sauvegardée dans la RAM, alors qu'un paramètre de valeur peut être ignoré.
  • Il est préférable de passer par valeur plutôt que de passer par référence uniquement pour copier le paramètre dans une variable locale et ne pas le toucher à nouveau.

Le verdict:

Il est beaucoup plus important de comprendre ce que ByVal et ByRef font réellement pour vous et de comprendre la différence entre les types valeur et référence, plutôt que de penser à la performance. La règle numéro un consiste à utiliser la méthode la plus appropriée à votre code .

Pour les types de grande valeur (plus de 64 bits), transmettez par référence sauf s'il est avantageux de passer par valeur (comme un code plus simple, "cela a simplement un sens" ou la cohérence de l'interface).

Pour les types de valeur plus petits, le mécanisme de transmission n'a pas beaucoup d'incidence sur les performances. De toute façon, il est difficile de prédire quelle méthode sera la plus rapide, car cela dépend de la taille de l'objet, de la manière dont l'appelant et l'appelé l'utilise. même des considérations de cache. Faites tout ce qui est logique pour votre code.

ByVal crée une copie de la variable, alors que ByRef transmet un pointeur. Je dirais donc que ByVal est plus lent (en raison du temps nécessaire pour copier) et utilise davantage de mémoire.

Ma curiosité était de vérifier les différents comportements en fonction des utilisations d'objet et de mémoire

Le résultat semble démontrer que ByVal l'emporte toujours, la ressource dépend de la collecte de mémoire ou moins (version 4.5.1 uniquement)

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top