どちらが速いですか?ByVal または ByRef?
-
03-07-2019 - |
質問
VB.NET では、メソッドの引数に使用する方が高速です。 ByVal
または ByRef
?
また、実行時により多くのリソース (RAM) を消費するのはどちらですか?
読み通しました この質問, 、しかし、答えは適切ではないか、十分に具体的ではありません。
解決
Byval引数とByRef引数は、速度と速度ではなく、それらがどのように機能するかの要件と知識に基づいて使用する必要があります。
http://www.developer.com/net/vb/article。 php / 3669066
スラウのコメントへの回答-
実行時に多くのリソースを消費しますか
パラメータはスタックで渡されます。スタックは非常に高速です。これは、メモリの割り当てが単に新しい「フレーム」を予約するためのポインタ増分であるためです。または「割り当てレコード」。ほとんどの.NETパラメータはマシンレジスタのサイズを超えないため、「スタック」があったとしてもわずかです。スペースを使用してパラメーターを渡します。実際、基本型とポインターは両方ともスタックに割り当てられます。 .NETのスタックサイズは1MBに制限されています。これにより、パラメータの受け渡しによって消費されるリソースがどれだけ少ないかがわかります。
この一連の記事は興味深いものです。
スタック割り当てによるパフォーマンスの改善(.NETメモリ管理:パート2)
どちらが速いですか? ByValまたはByRef。
測定のコンテキストによっては、正確に妖精を測定することはせいぜい難しいですが、メソッドを1億回呼び出すと書いたベンチマークは次のようになりました:
- 参照タイプ-参照渡し:420ミリ秒
- 参照タイプ-渡されるVal: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
各メソッドでの変数と割り当てのコメントアウト-
- 参照タイプ-参照渡し:389ミリ秒
- 参照タイプ-渡されるByVal:349ミリ秒
- 値タイプ-参照渡し:416ミリ秒
- 値のタイプ-ByValで渡される:385ミリ秒
ByVal参照型(文字列、クラス)を渡すことで時間を節約できると結論付けることができます。また、値型(整数、バイト)を渡す-ByValを使用すると時間を節約できます。
時間の繰り返しは、物事の大規模な計画では無視できます。さらに重要なのは、ByValとByRefを適切に使用し、「舞台裏」で何が起こっているのかを理解することです。ルーチンに実装されたアルゴリズムは、プログラムのランタイムに何倍も影響を与えます。
他のヒント
非常に大きな値型(たとえば、Guidはかなり大きい)を使用している場合、参照によってパラメーターを渡す方がわずかに高速になる場合があります。他の場合、値ではなく参照で渡すと、コピーなどがより多くあります。たとえば、バイトパラメータを持っている場合、1バイトは明らかに4または8バイト未満です。参照で渡した場合、ポインターはそれを取得します。
実際には、これについて心配することはほとんどありません。可能な限り読みやすいコードを記述します。これは、ほとんどの場合、参照ではなく値でパラメーターを渡すことを意味します。 ByRefはほとんど使用しません。
パフォーマンスを改善し、ByRefが役立つと思う場合は、コミットする前に(正確な状況で)慎重にベンチマークしてください。
編集:別の(以前は受け入れられ、現在削除されている)回答へのコメントで、値型に関してByRefとByValが何を意味するかについて多くの誤解があることに注意します。 パラメータの受け渡しに関する記事があります。これは長年にわたって人気がありました-C#用語ですが、同じ概念がVB.NETに適用されます。
状況によります。オブジェクトを渡す場合は、既にポインターを渡しています。 (たとえば)ArrayListを渡して、メソッドがArrayListに何かを追加する場合、呼び出し元のコードも同じArrayListであるため、渡されたArrayListに同じオブジェクトを持っています。ポインターを渡さないのは、intやdoubleなどの組み込みデータ型の変数を関数に渡すときだけです。その時点で、コピーが作成されます。ただし、これらのオブジェクトのデータサイズは非常に小さいため、メモリ使用量または実行速度の点でほとんど違いはありません。
参照型を渡す場合、ByRef は遅くなります。
これは、渡されるのがポインターへのポインターであるためです。オブジェクト上のフィールドにアクセスするには、追加のポインタを逆参照する必要があり、完了するまでにさらに数クロック サイクルがかかります。
値の型を渡す場合、構造体に多くのメンバーがある場合、byref はスタック上の値をコピーするのではなく単一のポインターのみを渡すため、より高速になる可能性があります。メンバーへのアクセスに関しては、byref は追加のポインター逆参照 (sp->pValueType->member と sp->member) を行う必要があるため遅くなります。
VB ではほとんどの場合、これについて心配する必要はありません。
.NET では、多数のメンバーを持つ値型が存在することはまれです。通常は小さいです。その場合、値型を渡すことは、プロシージャに複数の引数を渡すことと変わりません。たとえば、Point オブジェクトを値で渡すコードがある場合、そのパフォーマンスは X 値と Y 値をパラメータとして受け取るメソッドと同じになります。DoSomething(x を整数として、y を整数として) を確認しても、おそらくパフォーマンスの問題は発生しません。実際、それについて二度考えることはおそらくないでしょう。
大きな値の型を自分で定義している場合は、それらを参照型に変換することを再考する必要があります。
その他の唯一の違いは、コードの実行に必要なポインター間接参照の数が増加することです。そのレベルでの最適化が必要になることはほとんどありません。ほとんどの場合、解決できるアルゴリズムの問題があるか、パフォーマンスのボトルネックがデータベースの待機やファイルへの書き込みなどの IO 関連であるかのどちらかです。この場合、ポインターの間接参照を排除してもあまり役に立ちません。
したがって、byval と byref のどちらが速いかに焦点を当てるのではなく、必要なセマンティクスを提供するものに実際に焦点を当てることをお勧めします。一般に、特に byref が必要でない限り、byval を使用することをお勧めします。これにより、プログラムが非常に理解しやすくなります。
.NETの内部についてはあまり知りませんが、コンパイルされた言語について知っていることについて説明します。これは参照型には適用されません。値型については完全に正確ではない場合があります。値型と参照型の違いがわからない場合、これを読むべきではありません。 32ビットx86(32ビットポインター)を想定します。
- 32ビットより小さい値を渡すと、スタック上の32ビットオブジェクトが引き続き使用されます。このオブジェクトの一部は「未使用」になりますまたは「パディング」。そのような値を渡すことは、32ビット値を渡すことよりも少ないメモリを使用しません。
- 32ビットより大きい値を渡すと、ポインターよりも多くのスタック領域が使用され、おそらくコピー時間が長くなります。
- オブジェクトが値で渡される場合、呼び出し先はスタックからオブジェクトを取得できます。オブジェクトが参照によって渡される場合、呼び出し先は最初にスタックからオブジェクトのアドレスを取得し、次に他の場所からオブジェクトの値を取得する必要があります。値によって、フェッチが1つ少なくなりますか?実際、フェッチは呼び出し元が行う必要がありますが、呼び出しが保存されているさまざまな理由で呼び出し元が既にフェッチしている必要がある場合があります。
- 明らかに、参照値への変更はすべてRAMに保存する必要がありますが、値によるパラメーターは破棄できます。
- パラメータをローカル変数にコピーし、再度触れないようにするためだけに参照渡しするよりも、値渡しする方が良いです。
判定:
パフォーマンスについて考えるよりも、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