39

VB.NET では、メソッドの引数にどちらを使用する方が速いですByValByRef?

また、実行時により多くのリソース (RAM) を消費するのはどれですか?


この質問を読みましたが、回答が適切でないか、十分に具体的ではありません。

4

7 に答える 7

129

Byval および ByRef 引数は、速度ではなく、要件と、それらがどのように機能するかについての知識に基づいて使用する必要があります。

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

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

実行時により多くのリソースを消費するのはどれですか?

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

このシリーズの記事は興味深いかもしれません。

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

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

測定のコンテキストにもよりますが、正確かつ妖精に測定することはせいぜい困難ですが、メソッドを1億回呼び出すベンチマークを書いたところ、次の結果が得られました。

  • 参照型 - Passed ByRef: 420 ミリ秒
  • 参照型 - 渡された ByVal: 382 ミリ秒
  • 値のタイプ - Passed 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

各メソッドの変数と代入をコメントアウト -

  • 参照型 - Passed ByRef: 389 ミリ秒
  • 参照型 - 渡された ByVal: 349 ミリ秒
  • 値のタイプ - Passed ByRef: 416 ミリ秒
  • 値のタイプ - 渡される ByVal: 385 ミリ秒

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

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

于 2009-01-02T22:02:03.830 に答える
30

非常に大きな値の型を使用している場合 (たとえば、Guid がかなり大きい場合)、パラメーターを参照で渡す方がわずかに高速になる場合があります。他の場合では、値渡しよりも参照渡しの方がコピーなどの回数が多くなる可能性があります。参照渡ししました。

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

パフォーマンスを向上させたい場合で、ByRef が役立つと思われる場合は、コミットする前に (実際の状況で) 慎重にベンチマークしてください

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

于 2009-01-02T22:03:03.517 に答える
11

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

于 2009-01-02T21:55:26.263 に答える
5

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

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

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

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

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

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

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

したがって、byval と byref のどちらが高速であるかに注目するのではなく、必要なセマンティクスを提供するものに注目することをお勧めします。一般に、特に byref が必要でない限り、byval を使用することをお勧めします。これにより、プログラムが非常に理解しやすくなります。

于 2009-01-02T22:12:43.357 に答える
2

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

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

評決:

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

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

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

于 2009-01-03T00:25:07.600 に答える
1

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

于 2009-01-02T21:47:39.927 に答える