Performances ByRef vs ByVal lors du passage de chaînes
-
26-09-2020 - |
Question
En lisant Lequel est plus vite?ByVal ou ByRef ? m'a fait me demander si les commentaires qui s'y trouvaient s'appliquaient à Strings
en termes de performances.Puisque les chaînes sont copiées avant d'être transmises, n'est-il pas beaucoup plus efficace (si l'appelé n'a pas besoin d'une copie du cours de chaînes) de transmettre des chaînes ByRef
?
Merci,
PCP.
Modifier: Considérez ce code, qui m'a fait penser qu'il y avait une sorte de copie en cours :
Sub Main()
Dim ByValStr As String = "Hello World (ByVal)!"
Dim ByRefStr As String = "Hello World (ByRef)!"
fooval(ByValStr)
fooref(ByRefStr)
Console.WriteLine("ByVal: " & ByValStr)
Console.WriteLine("ByRef: " & ByRefStr)
Console.ReadLine()
End Sub
Sub fooval(ByVal Str As String)
Str = "foobar"
End Sub
Sub fooref(ByRef Str As String)
Str = "foobar"
End Sub
Il produit :
ByVal: Hello World (ByVal)!
ByRef: foobar
La solution
Cordes ne sont pas copié avant d'être transmis.Les chaînes sont des types référence, même si elles se comportent un peu comme des types valeur.
Vous devez utiliser ce qui est le plus logique dans le contexte de vos besoins.(Et si vos exigences sont quelque chose comme "vous devez exploiter chaque dernière nanoseconde de performances au détriment de toutes les autres considérations", alors vous devriez probablement pirater le profileur plutôt que de demander sur stackoverflow !)
C'est presque certainement quelque chose dont vous n'avez pas à vous inquiéter, et je doute qu'il y ait jamais une différence de performances significative.La seule situation où je peux voir une chance de différence serait lors du dépassement grand types de valeur.
Autres conseils
J'ai décidé de vérifier cela par moi-même, pour obtenir une réponse plus "scientifique".Ce sont les mêmes.Si j'utilise le code ci-dessous, ByVal est environ 2% plus lent que ByRef.Si, toutefois, je les échange, de sorte que je chronomètre ByRef avant ByVal, alors ByRef est environ 2 % plus lent.Donc, ce qui compte plus que ByRef ou ByVal dans ce cas, c'est l'ordre dans lequel ils s'exécutent :)
Function CreateString()
Dim i As Integer
Dim str As String = ""
For i = 1 To 10000
str = str & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Next i
Return str
End Function
Sub fooval(ByVal Str As String)
Str = Str & "foobar"
End Sub
Sub fooref(ByRef Str As String)
Str = Str & "foobar"
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim str As String = CreateString()
Dim stopWatch As New Stopwatch
Dim i As Integer
stopWatch.Start()
For i = 1 To 1000
fooval(str)
Next i
stopWatch.Stop()
Dim valtime As Long = stopWatch.ElapsedMilliseconds
stopWatch.Restart()
For i = 1 To 1000
fooref(str)
Next i
stopWatch.Stop()
Dim reftime As Long = stopWatch.ElapsedMilliseconds
MsgBox("Val took " & valtime & " milliseconds and Ref took " & reftime & " milliseconds")
End Sub
Pour comprendre le comportement des types de classe, y compris les chaînes, tenez compte de tous les paramètres, variables, champs et éléments de tableau de type classe, etc.comme contenant des "identifiants d'objet".Si Foo
est une variable de type string
, la déclaration Foo = 12345.ToString();
créera un nouvel identifiant d'objet (hypothétiquement, l'ID d'objet n° 197) et créera un nouvel objet de type string
avec cet identifiant, contenant les cinq caractères "12345"
.Il stockera alors Object ID#197
dans la variable Foo
.Si on appelle une routine avec un paramètre non-ref param
, et passe Foo
à cela, alors param
sera une variable locale contenant Object ID #197
.La déclaration param += "6";
créerait un nouvel objet (par ex.ID d'objet #521), de type chaîne, contenant les six caractères "123456"
et stocker Object ID #521
dans param
.Noter que Foo
tient toujours Object ID#197
, et cet objet contient toujours la chaîne de cinq caractères "12345"
.
Si param
avait été dépassé ref
, alors la déclaration param += "6"
aurait stocké Object ID #521
dans Foo
.Cela n'aurait toujours pas provoqué de changement observable sur l'objet n° 197, sauf peut-être pour le rendre éligible au garbage collection (si Foo
avait été la seule référence à l'objet n°197, l'écraser signifierait qu'il n'existerait plus aucune référence à cet objet nulle part dans l'univers).
Notez qu'il est généralement assez facile de raisonner sur des types de classes immuables comme string
, même sans penser en termes d'ID d'objet, puisque la seule façon de modifier la séquence de caractères représentée par une variable chaîne est d'y stocker un ID d'objet différent.Cependant, penser en termes d'ID d'objet devient essentiel lorsqu'il s'agit de types de classes mutables.Passer une variable de type classe Car
, et non par référence, équivaudrait à copier un VIN d'un bout de papier à un autre, et à remettre ce dernier bout de papier à certains employés du magasin et à leur demander d'en faire quelque chose.Si le premier document identifiait à l'origine une voiture rouge avec le VIN#15934, alors lorsque les travailleurs auraient terminé, le premier document pourrait identifier une voiture bleue avec le VIN#15934, mais ce serait la même voiture.Rien de ce que les travailleurs pouvaient faire avec le bout de papier qui leur était donné, ni rien de ce qu'ils pouvaient faire avec la voiture, ne changerait la voiture à laquelle le premier papier faisait référence.D'un autre côté, transmettre le paramètre par référence reviendrait davantage aux employés de l'atelier à recevoir un morceau de papier sur lequel le VIN est écrit et à récupérer le papier une fois qu'ils auront terminé.Si les travailleurs pouvaient rayer le NIV et en écrire un autre, alors lorsqu'ils rendraient le bout de papier, il pourrait faire référence à la même voiture ou à une autre voiture ;s'il fait référence à une voiture différente, la voiture à laquelle elle faisait initialement référence peut ou non avoir été modifiée, et la voiture à laquelle le document finit par faire référence peut ou non ressembler à l'original.