web-dev-qa-db-ja.com

どちらが速いですか? ByValまたはByRef?

メソッド引数に使用する方が速いVB.NETでは、ByValまたはByRef

また、どちらが実行時に多くのリソースを消費しますか(RAM)?


この質問 を読み通しましたが、答えは当てはまらないか、十分に具体的ではありません。

35
Robinicks

Byval引数とByRef引数は、速度ではなく要件ではなく要件と知識に基づいて使用する必要があります。

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

スラウのコメントに応えて-

どちらが実行時に多くのリソースを消費しますか?

パラメータはスタックに渡されます。スタックは非常に高速です。これは、そのメモリ割り当てが、新しい「フレーム」または「割り当てレコード」を予約するための単なるポインタインクリメントであるためです。ほとんどの.NETパラメータはマシンレジスタのサイズを超えないため、パラメータの受け渡しに「スタック」スペースが使用されている場合はほとんどありません。実際、基本的な型とポインタは両方ともスタックに割り当てられます。 .NETのスタックサイズは1MBに制限されています。これにより、パラメーターの受け渡しによって消費されるリソースがどれだけ少ないかがわかります。

この一連の記事は興味深いかもしれません。

スタック割り当てによるパフォーマンスの向上(.NETメモリ管理:パート2)

どちらが速いですか? ByValまたはByRef。

正確にそして妖精を測定することはせいぜい難しいです-あなたの測定の文脈にもよるが、私が1億回メソッドを呼び出して書いたベンチマークは次のことを思いついた:

  • 参照タイプ-PassedByRef:420ミリ秒
  • 参照タイプ-PassedByVal:382ミリ秒
  • 値のタイプ-PassedByRef:421ミリ秒
  • 値のタイプ-PassedByVal: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

各メソッドでの変数と割り当てのコメント化-

  • 参照タイプ-PassedByRef:389ミリ秒
  • 参照タイプ-PassedByVal:349ミリ秒
  • 値のタイプ-PassedByRef:416ミリ秒
  • 値のタイプ-PassedByVal:385ミリ秒

参照型(文字列、クラス)を渡すことで、時間を節約できると結論付けることができます。値型(整数、バイト)を渡すと言うかもしれません-ByValは時間を節約します。

繰り返しますが、物事の壮大な計画では時間はごくわずかです。さらに重要なのは、ByValとByRefを適切に使用し、「舞台裏」で何が起こっているのかを理解することです。ルーチンに実装されているアルゴリズムは、プログラムの実行時間に何倍も影響を与えることは間違いありません。

126
user50612

非常に大きな値型を使用している場合(たとえば、Guidがかなり大きい場合)、参照によってパラメーターを渡す方がわずかに速い場合があります。他のケースでは、値渡しよりも参照渡しの場合moreコピーなどがある可能性があります。たとえば、バイトパラメータがある場合、 1バイトは、ポインタを参照渡しした場合にポインタが取る4バイトまたは8バイトよりも明らかに小さいです。

実際には、これについて心配する必要はほとんどありません。可能な限り読み取り可能なコードを記述します。これは、ほとんどの場合、参照ではなく値でパラメーターを渡すことを意味します。 ByRefを使用することはめったにありません。

パフォーマンスを向上させ、ByRefが役立つと思う場合は、コミットする前に(正確な状況で)pleaseベンチマークを慎重に行ってください。

編集:別の(以前は受け入れられ、現在は削除されている)回答へのコメントで、値型に関してByRefとByValの意味について多くの誤解があることに注意します。私は パラメーターの受け渡しに関する記事 を持っています。これは長年にわたって人気が高いことが証明されています。これはC#の用語にありますが、VB.NETにも同じ概念が適用されます。

30
Jon Skeet

場合によります。オブジェクトを渡す場合、そのオブジェクトはすでにポインタを渡しています。そのため、たとえばArrayListを渡して、メソッドがArrayListに何かを追加した場合、呼び出し元のコードも、同じArrayListであるため、渡されたArrayListに同じオブジェクトを持ちます。ポインターを渡さないのは、intやdoubleなどの組み込みデータ型の変数を関数に渡すときだけです。その時点で、コピーが作成されます。ただし、これらのオブジェクトのデータサイズは非常に小さいため、メモリ使用量や実行速度の点でどちらの方法でもほとんど違いはありません。

11
Kibbee

参照型を渡す場合、ByRefは遅くなります。

これは、渡されるのはポインタへのポインタだからです。オブジェクトのフィールドにアクセスするには、追加のポインタを逆参照する必要があります。これには、完了するまでに数クロックサイクル余分にかかります。

値の型を渡す場合、構造体に多くのメンバーがあると、スタック上の値をコピーするのではなく、単一のポインターのみを渡すため、byrefの方が高速になる可能性があります。メンバーへのアクセスに関しては、追加のポインター逆参照(sp-> pValueType-> memberとsp-> member)を実行する必要があるため、byrefは遅くなります。

ほとんどの場合、VBではこれについて心配する必要はありません。

.NETでは、多数のメンバーを持つ値型を持つことはめったにありません。それらは通常小さいです。その場合、値型を渡すことは、プロシージャに複数の引数を渡すことと同じです。たとえば、Pointオブジェクトを値で渡すコードがある場合、そのパフォーマンスは、X値とY値をパラメーターとして取るメソッドと同じになります。 DoSomething(xを整数、yを整数)を見ても、おそらくパフォーマンスの問題は発生しません。実際、あなたはおそらくそれについて二度考えたことはないでしょう。

大きな値型を自分で定義している場合は、おそらくそれらを参照型に変換することを再検討する必要があります。

他の唯一の違いは、コードの実行に必要なポインターの間接化の数の増加です。そのレベルで最適化する必要があることはめったにありません。ほとんどの場合、対処できるアルゴリズムの問​​題があるか、パフォーマンスのボトルネックがIO関連、たとえばデータベースの待機やファイルへの書き込みなど)のいずれかである場合、ポインターの間接参照を排除することはできませんあまり役に立ちません。

したがって、byvalまたはbyrefの方が速いかどうかに焦点を合わせるのではなく、必要なセマンティクスを提供するものに本当に焦点を当てるように勧めます。一般に、特にbyrefが必要でない限り、byvalを使用することをお勧めします。それはプログラムをはるかに理解しやすくします。

5

.NETの内部についてはよくわかりませんが、コンパイル言語について知っていることについて説明します。これは参照型には適用されず、値型について完全に正確ではない場合があります。値の型と参照の型の違いがわからない場合は、これを読んではいけません。 32ビットx86(32ビットポインター付き)を想定します。

  • 32ビットより小さい値を渡すと、スタック上の32ビットオブジェクトが引き続き使用されます。このオブジェクトの一部は「未使用」または「パディング」になります。このような値を渡すことは、32ビット値を渡すよりも少ないメモリを使用しません。
  • 32ビットより大きい値を渡すと、ポインターよりも多くのスタックスペースが使用され、おそらくコピー時間が長くなります。
  • オブジェクトが値で渡される場合、呼び出し先はスタックからオブジェクトをフェッチできます。オブジェクトが参照によって渡される場合、呼び出し先は最初にスタックからオブジェクトのアドレスをフェッチし、次に他の場所からオブジェクトの値をフェッチする必要があります。値とは、フェッチが1つ少ないことを意味します。まあ、実際にはフェッチは呼び出し側で行う必要があります-ただし、呼び出しがフェッチされた場合は、さまざまな理由でフェッチが保存されている可能性があります。
  • 明らかに、参照による値に加えられた変更はすべてRAMに保存する必要がありますが、値によるパラメータは破棄できます。
  • パラメータをローカル変数にコピーしてそれを再度操作するのではなく参照で渡すよりも、値で渡す方が適切です。

評決:

パフォーマンスについて考えるよりも、ByValとByRefが実際に何をするのかを理解し、値と参照タイプの違いを理解することがはるかに重要です。一番のルールはコードに適した方法を使用するです。

大きな値型(64ビットを超える)の場合、値渡し(より単純なコード、「意味が通じる」、またはインターフェイスの一貫性など)による利点がない限り、参照渡しを行います。

小さい値型の場合、受け渡しメカニズムはパフォーマンスに大きな違いはありません。オブジェクトのサイズ、呼び出し元と呼び出し先がオブジェクトをどのように使用するか、さらにはキャッシュの考慮事項にも依存するため、どちらの方法が高速になるかを予測するのは困難です。 。コードにとって意味のあることは何でもしてください。

2
Artelius

ByValは変数のコピーを作成しますが、ByRefはポインターを渡します。したがって、ByValは(コピーに時間がかかるため)遅くなり、より多くのメモリを使用することになります。

1
Colin

私の好奇心は、オブジェクトとメモリの使用量に応じてさまざまな動作を確認することでした

結果は、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
0
Fabio Guerrazzi