Производительность ByRef и ByVal при передаче строк
-
26-09-2020 - |
Вопрос
Чтение Что быстрее?ByVal или ByRef? заставило меня задуматься, относятся ли комментарии там к Strings
с точки зрения производительности.Поскольку строки копируются перед передачей, не гораздо ли эффективнее (если вызываемому абоненту не нужна копия курса строк) передавать строки? ByRef
?
Спасибо,
КФП.
Редактировать: Рассмотрим этот код, который заставил меня подумать, что происходит какое-то копирование:
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
Он выводит:
ByVal: Hello World (ByVal)!
ByRef: foobar
Решение
Струны не скопировано перед передачей.Строки являются ссылочными типами, хотя они ведут себя как типы значений.
Вам следует использовать то, что имеет наибольший смысл в контексте ваших требований.(И если ваши требования сводятся к чему-то вроде «необходимо выжать каждую последнюю наносекунду производительности за счет всех других соображений», то вам, вероятно, следует использовать профилировщик, а не спрашивать о stackoverflow!)
Об этом почти наверняка не стоит беспокоиться, и я сомневаюсь, что когда-либо будет существенная разница в производительности.Единственная ситуация, в которой я вижу хоть какой-то шанс на разницу, — это прохождение большой типы значений.
Другие советы
Я решил проверить это на себе, чтобы получить более «научный» ответ.Они одинаковые.Если я использую код ниже, ByVal будет примерно на 2% медленнее, чем ByRef.Однако если я поменяю их местами так, что я буду синхронизировать ByRef перед ByVal, то ByRef будет примерно на 2% медленнее.Итак, что на самом деле имеет большее значение, чем ByRef или ByVal, в этом случае — это порядок, в котором они выполняются :)
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
Чтобы понять поведение типов классов, включая строки, обратите внимание на все параметры типа класса, переменные, поля, элементы массива и т. д.как хранение «идентификаторов объектов».Если Foo
это переменная типа string
, заявление Foo = 12345.ToString();
создаст новый идентификатор объекта (гипотетически, идентификатор объекта № 197) и создаст новый объект типа string
с этим идентификатором, содержащим пять символов "12345"
.Затем он будет хранить Object ID#197
в переменную Foo
.Если кто-то вызывает подпрограмму с параметром, отличным от ссылки param
, и проходит Foo
к этому, тогда param
будет локальной переменной, содержащей Object ID #197
.Заявление param += "6";
создаст новый объект (например.Идентификатор объекта № 521) строкового типа, содержащий шесть символов. "123456"
и хранить Object ID #521
в param
.Обратите внимание, что Foo
все еще держится Object ID#197
, и этот объект по-прежнему содержит строку из пяти символов "12345"
.
Если param
прошел мимо ref
, то утверждение param += "6"
хранил бы Object ID #521
в Foo
.Это все равно не привело бы к каким-либо заметным изменениям в Объекте № 197, за исключением, возможно, того, что он стал бы пригодным для сбора мусора (если Foo
была единственной ссылкой на Объект № 197, ее перезапись означало бы, что нигде во вселенной больше не будет никаких ссылок на этот объект).
Обратите внимание, что, как правило, довольно легко рассуждать о неизменяемых типах классов, таких как string
, даже не задумываясь об идентификаторах объектов, поскольку единственный способ изменить последовательность символов, представленную строковой переменной, — это сохранить там другой идентификатор объекта.Однако мышление в терминах идентификаторов объектов становится важным при работе с изменяемыми типами классов.Передача переменной типа класса Car
, а не по ссылке, было бы эквивалентно копированию VIN-кода с одного листа бумаги на другой и передаче последнего листа бумаги некоторым работникам магазина и просьбе их что-то с ним сделать.Если в первом документе изначально была указана красная машина с VIN-кодом 15934, то, когда рабочие закончили, в первом документе могла быть указана синяя машина с VIN-кодом 15934, но это была бы та же самая машина.Ничто из того, что рабочие могли сделать с листком бумаги, который им дали, или что-либо, что они могли сделать с машиной, не изменило бы то, о какой машине говорилось в первой газете.С другой стороны, передача параметра по ссылке была бы больше похожа на то, как работники магазина передают лист бумаги с написанным на нем VIN и получают от них эту бумагу обратно, когда они закончат.Если бы рабочие могли вычеркнуть VIN и написать другой, то, когда они вернут листок бумаги, он мог бы относиться к той же машине или к другой машине;если речь идет о другом автомобиле, автомобиль, о котором первоначально говорилось, мог быть или не быть модифицирован, а автомобиль, о котором в конечном итоге говорится в статье, может иметь или не иметь никакого сходства с оригиналом.