Подтвердить что ты не робот

Что быстрее? ByVal или ByRef?

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

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


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

4b9b3361

Ответ 1

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

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

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

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

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

Вы можете найти эту интересную статью:

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

Что быстрее? ByVal или ByRef.

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

  • Тип ссылки - пройден ByRef: 420 мс
  • Тип ссылки - передано ByVal: 382 мс
  • Тип значения - пройден ByRef: 421 мс
  • Тип значения - передано ByVal: 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

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

  • Тип ссылки - передано ByRef: 389 мс
  • Тип ссылки - передано ByVal: 349 мс
  • Тип значения - пройден ByRef: 416 мс
  • Тип значения - передано ByVal: 385 мс

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

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

Ответ 2

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

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

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

EDIT: я отмечаю в комментариях к другому (ранее принятому, теперь удаленному) ответу, что существует много недоразумений относительно того, что означает ByRef vs ByVal, когда дело касается типов значений. У меня есть статья о передаче параметров, которая на протяжении многих лет оказалась популярной - она ​​в терминологии С#, но те же понятия применимы к VB.NET.

Ответ 3

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

  • Передача значений, меньших 32 бит, по-прежнему использует 32-битный объект в стеке. Часть этого объекта будет "неиспользуемым" или "дополнением". Передача таких значений не использует меньше памяти, чем передача 32-битных значений.
  • Передача значений, превышающих 32 бита, будет использовать больше пространства стека, чем указатель, и, вероятно, больше времени копирования.
  • Если объект передается по значению, вызываемый может извлечь объект из стека. Если объект передается по ссылке, вызывающий должен сначала получить адрес объекта из стека, а затем извлечь значение объекта из другого места. По значению означает, что на выбор меньше, верно? Ну, на самом деле, выбор должен выполняться вызывающим абонентом, однако вызывающему абоненту, возможно, уже приходилось брать по разным причинам, и в этом случае сохраняется выборка.
  • Очевидно, что любые изменения, сделанные для ссылочного значения, должны быть сохранены обратно в ОЗУ, тогда как параметр байтового значения может быть отброшен.
  • Лучше передавать по значению, чем передавать по ссылке только для копирования параметра в локальную переменную и не касаться его снова.

Приговор:

Гораздо важнее понять, что на самом деле делают ByVal и ByRef для вас, и понимать разницу между значениями и ссылочными типами, чем думать о производительности. Правило номер один: использовать любой метод, более подходящий для вашего кода.

Для больших типов значений (более 64 бит) передайте по ссылке, если нет преимущества для передачи по значению (например, более простой код, "это просто имеет смысл" или согласованность интерфейса).

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

Ответ 6

ByVal создает копию переменной, тогда как ByRef передает указатель. Поэтому я бы сказал, что ByVal медленнее (из-за времени, которое требуется для копирования) и использует больше памяти.

Ответ 7

Мое любопытство заключалось в том, чтобы проверять различные типы поведения, связанные с использованием объектов и памяти

Результат кажется демостратом, что 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