Что в VB.NET быстрее использовать для аргументов метода: ByVal
или ByRef
?
Кроме того, что потребляет больше ресурсов во время выполнения (ОЗУ)?
Я прочитал этот вопрос, но ответы не применимы или не являются конкретными достаточно.
Что в VB.NET быстрее использовать для аргументов метода: ByVal
или ByRef
?
Кроме того, что потребляет больше ресурсов во время выполнения (ОЗУ)?
Я прочитал этот вопрос, но ответы не применимы или не являются конкретными достаточно.
Аргументы Byval и ByRef следует использовать на основе требований и знания того, как они работают, не со скоростью.
http://www.developer.com/net/vb/article.php/3669066
В ответ на комментарий Slough -
Что потребляет больше ресурсов во время выполнения?
Параметры передаются в стек. Стек работает очень быстро, потому что его выделение памяти - это просто приращение указателя для резервирования нового «кадра» или «записи распределения». Большинство параметров .NET не превышают размер машинного регистра, поэтому для передачи параметров используется небольшое пространство «стека», если оно вообще есть. Фактически, базовые типы и указатели размещаются в стеке. Размер стека в .NET ограничен 1 МБ. Это должно дать вам представление о том, сколько ресурсов потребляется при передаче параметров.
Возможно, вам будет интересна эта серия статей:
Повышение производительности за счет выделения стека (Управление памятью .NET: часть 2)
Что быстрее? ByVal или ByRef.
В лучшем случае сложно измерить точно и волшебно - в зависимости от контекста вашего измерения, но тест, который я написал, вызывая метод 100 миллионов раз, дал следующее:
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
Комментируя переменную и присваивание в каждом методе -
Можно сделать вывод, что передача ссылочных типов (строки, классы) ByVal сэкономит время. Вы также можете сказать, что передача типов значений (целые, байтовые) - ByVal сэкономит некоторое время.
Опять же, по большому счету время ничтожно мало. Что более важно, так это правильное использование ByVal и ByRef и понимание того, что происходит «за кулисами». Алгоритмы, реализованные в ваших подпрограммах, наверняка повлияют на время выполнения вашей программы во много раз больше.
ByVal
, будет варьироваться в зависимости от размера рассматриваемого типа значения. Времени на прохождение ByRef
не будет. Если типы значений большие, передача ByRef
может быть намного быстрее. Обратите внимание, что даже типы с очень большими значениями (тысячи байтов) могут быть очень производительными, если можно избежать передачи их по значению или ненужного копирования.
- person supercat; 28.06.2012
Byte()
ByVal
быстрее, чем ByRef
, но также незначительно. Я изменил этот тест, чтобы использовать Byte()
вместо String
, и запустил его с массивом байтов 6 МБ. ByRef
был 331 мс, а ByVal
был 231 мс. Это ничтожно мало: разница в 0,000001 мс на вызов.
- person GreenRaccoon23; 10.12.2020
Если вы используете тип с очень большим значением (например, Guid довольно большой), может быть немного быстрее передать параметр по ссылке. В других случаях при передаче по ссылке может быть больше копий, чем по значению - например, если у вас есть байтовый параметр, то один байт явно меньше четырех или восьми байтов. что указатель примет, если вы передадите его по ссылке.
На практике вам почти никогда не стоит об этом беспокоиться. Напишите максимально читаемый код, что почти всегда означает передачу параметров по значению, а не по ссылке. Очень редко использую ByRef.
Если вы хотите повысить производительность и думаете, что ByRef поможет вам, пожалуйста тщательно протестируйте его (в вашей конкретной ситуации), прежде чем переходить к нему.
РЕДАКТИРОВАТЬ: в комментариях к другому (ранее принятому, теперь удаленному) ответу я отмечаю, что существует большое недопонимание того, что означает ByRef vs ByVal, когда речь идет о типах значений. У меня есть статья о передаче параметров, которая за многие годы стала популярной - она написана на C #. терминология, но те же концепции применимы и к VB.NET.
По-разному. Если вы передаете объект, он уже передает указатель. Вот почему, если вы передаете ArrayList (например), и ваш метод добавляет что-то в ArrayList, тогда вызывающий код также имеет тот же объект в его ArrayList, который был передан, потому что это тот же самый ArrayList. Единственный раз, когда он не передает указатель, - это когда вы передаете в функцию переменную с внутренним типом данных, например int или double. В этот момент он создает копию. Однако размер данных этих объектов настолько мал, что вряд ли будет иметь значение в любом случае с точки зрения использования памяти или скорости выполнения.
Если вы передаете ссылочный тип, ByRef работает медленнее.
Это потому, что передается указатель на указатель. Любой доступ к полям объекта требует разыменования дополнительного указателя, для завершения которого потребуется несколько дополнительных тактов.
Если вы передаете тип значения, то byref может быть быстрее, если структура имеет много членов, потому что он передает только один указатель, а не копирует значения в стеке. Что касается доступа к членам, byref будет медленнее, потому что ему нужно выполнить дополнительное разыменование указателя (sp-> pValueType-> member vs sp-> member).
В большинстве случаев в VB вам не о чем беспокоиться.
В .NET редко встречаются типы значений с большим количеством членов. Обычно они небольшие. В этом случае передача типа значения ничем не отличается от передачи нескольких аргументов в процедуру. Например, если у вас есть код, который передается в объекте Point по значению, он будет таким же, как метод, принимающий значения X и Y в качестве параметров. Просмотр DoSomething (x как целое число, y как целое число), вероятно, не вызовет проблем с производительностью. На самом деле, вы, вероятно, никогда не задумаетесь дважды.
Если вы сами определяете типы с большими значениями, вам, вероятно, следует пересмотреть их превращение в ссылочные типы.
Единственное другое отличие - это увеличение количества косвенных указателей, необходимых для выполнения кода. Редко когда вам когда-либо понадобится оптимизация на таком уровне. В большинстве случаев есть либо алгоритмические проблемы, которые вы можете решить, либо узкое место вашей производительности связано с вводом-выводом, например, ожидание базы данных или запись в файл, и в этом случае устранение косвенных указателей вам не очень поможет.
Итак, вместо того, чтобы сосредоточиться на том, быстрее ли byval или byref, я бы рекомендовал вам действительно сосредоточиться на том, что дает вам нужную семантику. В общем, рекомендуется использовать byval, если вам специально не нужен byref. Это значительно упрощает понимание программы.
Хотя я мало что знаю о внутреннем устройстве .NET, я расскажу о том, что я знаю о компилируемых языках. Это не применяется к ссылочным типам и может быть неточным в отношении типов значений. Если вы не знаете разницы между типами значений и ссылочными типами, вам не следует это читать. Я предполагаю 32-битный x86 (с 32-битными указателями).
Вердикт:
Намного важнее понять, что на самом деле делают для вас ByVal и ByRef, и понять разницу между типами значений и ссылочными типами, чем думать о производительности. Правило номер один - используйте тот метод, который больше подходит вашему коду.
Для типов с большими значениями (более 64 бит) передайте по ссылке, если нет преимуществ передачи по значению (например, более простой код, «это просто имеет смысл» или согласованность интерфейса).
Для типов с меньшими значениями механизм передачи не имеет большого значения для производительности, и в любом случае трудно предсказать, какой метод будет быстрее, поскольку это зависит от размера объекта, того, как вызывающий и вызываемый объект используют объект, и даже соображений кеширования. . Просто делайте то, что имеет смысл для вашего кода.
ByVal
создает копию переменной, а ByRef
передает указатель. Поэтому я бы сказал, что ByVal
работает медленнее (из-за времени, необходимого для копирования) и использует больше памяти.
Мое любопытство заключалось в том, чтобы проверить различное поведение в зависимости от использования объекта и памяти.
Результат, кажется, демонстрирует, что ByVal всегда побеждает, ресурс зависит от сбора памяти или меньше (только 4.5.1)
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