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 applicables ou suffisamment spécifiques.
Les arguments Byval et ByRef doivent être utilisés en fonction des besoins et de la connaissance de leur fonctionnement non sur la vitesse.
http://www.developer.com/net/vb/article.php/3669066
En réponse à un commentaire de Slough -
Qu'est-ce qui consomme plus de ressources au moment de 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 permettant de réserver un nouvel "enregistrement" ou "enregistrement d'allocation". La plupart des paramètres .NET ne dépassent pas la taille d'un registre de machine, si peu d'espace est utilisé pour transmettre les 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 Mo. 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: Partie 2)
Lequel est le plus rapide? ByVal ou ByRef.
Au mieux, il est difficile de mesurer avec précision et fée - en fonction du contexte de votre mesure, mais un critère que j'ai écrit, appelant une méthode 100 millions de fois, a donné les résultats suivants:
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
Mise en commentaire de la variable et affectation dans chaque méthode -
On pourrait en conclure que le passage de types de références (chaînes, classes) à ByVal vous fera gagner du temps. Vous pourriez aussi dire que 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 "en coulisse". Les algorithmes mis en œuvre dans vos routines affecteront sûrement beaucoup plus l'exécution de votre programme.
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 more copier etc. lorsque vous passez par référence plutôt que par valeur - par exemple, si vous avez un paramètre d’octet, un octet est clairement inférieur aux quatre ou huit octets 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 peut vous aider, please testez-le avec précaution (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 y a beaucoup de malentendus sur ce que ByRef vs ByVal signifie quand il s'agit de types de valeur. J'ai un article sur le passage de paramètres qui a fait ses preuves au fil des ans - il est en terminologie C #, mais les mêmes concepts s'appliquent à VB.NET.
Ça 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 contient également le même objet que ArrayList, car il s'agit du 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. Cependant, 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.
En effet, 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, 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 termes d'accès aux membres, byref sera plus lent car il doit effectuer une déréférence de pointeur supplémentaire (sp-> pValueType-> member vs sp-> membre).
La plupart du temps dans VB vous ne devriez pas vous inquiéter à ce sujet.
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 penseriez probablement jamais à deux fois.
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, il existe des problèmes d'algorithmique que vous pouvez résoudre, ou votre goulot d'étranglement de perf est lié à IO, comme l'attente de une base de données ou l'écriture dans un fichier, auquel cas éliminer les indirections de pointeur ne vous aidera pas beaucoup.
Donc, au lieu de vous concentrer plus rapidement sur byval ou byref, 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 complètement 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 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 est de utiliser la méthode qui convient le mieux à votre code .
Pour les types de grande valeur (plus de 64 bits), transmettez par référence à moins qu'il ne soit avantageux de transmettre par valeur (par exemple, un code plus simple, "cela a du 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évoir 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'utilisent et même de considérations relatives au cache . Faites juste ce qui a du sens 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 plus de mémoire.
Ma curiosité était de vérifier les différents comportements en fonction des usages des objets et de la mémoire
Le résultat semble démontrer que ByVal gagne toujours, la ressource dépend si collecter de la mémoire ou moins (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