Вопрос

В VB.NET, который быстрее использовать для аргументов метода, ByVal или ByRef?

Кроме того, что потребляет больше ресурсов во время выполнения (оперативной памяти)?


Я дочитал до конца этот вопрос, но ответы неприменимы или недостаточно конкретны.

Это было полезно?

Решение

Аргументы Byval и ByRef следует использовать на основе требований и знания того, как они работают нет о скорости.

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

В ответ на комментарий Слау -

Что потребляет больше ресурсов во время выполнения?

Параметры передаются по стеку.Стек работает очень быстро, потому что его выделение памяти - это просто увеличение указателя для резервирования нового "фрейма" или "записи выделения". Большинство параметров .NET не так уж и мало превышают размер машинного регистра, если для передачи параметров используется какое-либо пространство "стека".Фактически, как базовые типы, так и указатели размещаются в стеке.Размер стека в .NET ограничен 1 МБ.Это должно дать вам представление о том, как мало ресурсов потребляется при передаче параметров.

Возможно, вам покажется интересной эта серия статей:

Повышение производительности за счет выделения стека (управление памятью .NET:Часть 2)

Что быстрее?БыВал или ByRef.

В лучшем случае трудно измерить точно и достоверно - в зависимости от контекста вашего измерения, но тест, который я написал, вызывая метод 100 миллионов раз, выдал следующее:

  • Ссылочный тип - Передается по ссылке:420 мс
  • Ссылочный тип - Передается путем ввода:382 мс
  • Тип значения - Передается по ссылке:421 мс
  • Тип значения - Передается путем ввода:416 мс
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

Закомментирование переменной и присваивания в каждом методе -

  • Ссылочный тип - Передается по ссылке:389 мс
  • Ссылочный тип - Передается путем ввода:349 мс
  • Тип значения - Передается по ссылке:416 мс
  • Тип значения - Передается путем ввода:385 мс

Можно было бы сделать вывод, что передача ссылочных типов (строк, классов) БыВал сэкономит некоторое время.Вы также могли бы сказать, что передача типов значений (целое число, байт) - ByVal сэкономит некоторое время.

Опять же, время ничтожно мало в общей схеме вещей.Что еще более важно, так это правильно использовать ByVal и ByRef и понимать, что происходит "за кулисами". Алгоритмы, реализованные в ваших подпрограммах, наверняка повлияют на время выполнения вашей программы во много раз больше.

Другие советы

Если вы используете очень большой тип значения (например, Guid довольно большой), передача параметра по ссылке может быть немного быстрее.В других случаях могут быть Еще копирование и т.д., когда вы передаете по ссылке, а не по значению - например, если у вас есть параметр byte, то один байт явно меньше, чем четыре или восемь байт, которые занял бы указатель, если бы вы передали его по ссылке.

На практике вам почти никогда не следует беспокоиться по этому поводу.Напишите больше всего читаемый возможен код, который почти всегда означает передачу параметров по значению вместо ссылки.Я использую ByRef очень редко.

Если вы хотите повысить производительность и думаете, что ByRef поможет вам, пожалуйста тщательно проанализируйте его (в вашей конкретной ситуации), прежде чем приступать к нему.

Редактировать:Я отмечаю в комментариях к другому (ранее принятому, теперь удаленному) ответу, что существует большое недопонимание относительно того, что означает ByRef против ByVal, когда речь заходит о типах значений.У меня есть статья о передаче параметров который оказался популярным на протяжении многих лет - он написан в терминологии C #, но те же концепции применимы и к VB.NET.

Это зависит от обстоятельств.Если вы передаете объект, он уже передает указатель.Вот почему, если вы передаете ArrayList (например) и ваш метод добавляет что-то к ArrayList, то вызывающий код также содержит тот же объект в своем ArrayList, который был передан, потому что это тот же ArrayList.Единственный раз, когда он не передает указатель, - это когда вы передаете в функцию переменную с внутренним типом данных, например int или double .В этот момент он создает копию.Однако размер данных этих объектов настолько мал, что это вряд ли что-то изменит в любом случае с точки зрения использования памяти или скорости выполнения.

Если вы передаете ссылочный тип, ByRef работает медленнее.

Это происходит потому, что то, что передается, является указателем на указатель.Любой доступ к полям объекта требует разыменования дополнительного указателя, для завершения которого потребуется несколько дополнительных тактов.

Если вы передаете тип значения, то byref может быть быстрее, если структура имеет много членов, потому что она передает только один указатель, а не копирует значения в стек.С точки зрения доступа к элементам, byref будет медленнее, потому что ему нужно выполнить дополнительное разыменование указателя (sp->pValueType-> member vs sp->member).

Большую часть времени в VB вам не нужно беспокоиться об этом.

В .NET редко встречаются типы значений с большим количеством членов.Обычно они небольшие.В этом случае передача типа значения ничем не отличается от передачи нескольких аргументов процедуре.Например, если бы у вас был код, который передавался в точечный объект по значению, его производительность была бы такой же, как у метода, который принимал значения X и Y в качестве параметров.Просмотр doSomething (x как целое число, y как целое число), вероятно, не вызовет проблем с производительностью.На самом деле, вы, вероятно, никогда бы дважды не задумались об этом.

Если вы сами определяете типы больших значений, то вам, вероятно, следует пересмотреть превращение их в ссылочные типы.

Единственным другим отличием является увеличение количества косвенных указателей, необходимых для выполнения кода.Редко бывает, что вам когда-либо понадобится оптимизация на таком уровне.В большинстве случаев существуют либо алгоритмические проблемы, которые вы можете устранить, либо узкое место вашей производительности связано с вводом-выводом, например, ожидание базы данных или запись в файл, и в этом случае устранение косвенных указателей вам не сильно поможет.

Итак, вместо того, чтобы сосредотачиваться на том, быстрее ли byval или byref, я бы порекомендовал вам действительно сосредоточиться на том, что дает вам необходимую семантику.В общем, это хорошая идея - использовать byval, если только вам специально не нужен byref .Это значительно упрощает понимание программы.

Хотя я мало что знаю о внутренностях .NET, я расскажу о том, что я знаю о скомпилированных языках.Это не применяется к ссылочным типам, и может быть не совсем точным в отношении типов значений.Если вы не знаете разницы между типами значений и ссылочными типами, вам не следует читать это.Я предполагаю, что 32-разрядный x86 (с 32-разрядными указателями).

  • Передача значений, меньших 32 бит, по-прежнему использует 32-битный объект в стеке.Часть этого объекта будет "неиспользуемой" или "дополняющей".Передача таких значений не требует меньше памяти, чем передача 32-битных значений.
  • Передача значений, превышающих 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top