文字列を渡すときの ByRef と ByVal のパフォーマンス
-
26-09-2020 - |
質問
読む どちらが速いですか?ByVal または ByRef? そこにあるコメントが当てはまるかどうか疑問に思った Strings
パフォーマンスの面で。文字列は渡される前にコピーされるので、(呼び出し先が文字列のコピーを必要としない場合はもちろん) 文字列を渡す方がはるかに効率的ではないでしょうか。 ByRef
?
ありがとう、
CFP。
編集: 次のコードを考えてみましょう。何らかのコピーが行われているのではないかと思いました。
Sub Main()
Dim ByValStr As String = "Hello World (ByVal)!"
Dim ByRefStr As String = "Hello World (ByRef)!"
fooval(ByValStr)
fooref(ByRefStr)
Console.WriteLine("ByVal: " & ByValStr)
Console.WriteLine("ByRef: " & ByRefStr)
Console.ReadLine()
End Sub
Sub fooval(ByVal Str As String)
Str = "foobar"
End Sub
Sub fooref(ByRef Str As String)
Str = "foobar"
End Sub
出力は次のとおりです。
ByVal: Hello World (ByVal)!
ByRef: foobar
解決
文字列 ではありません 渡される前にコピーされます。文字列は値型と似た動作をしますが、参照型です。
要件のコンテキストで最も意味のあるものを使用する必要があります。(そして、もしあなたの要件が「他のすべての考慮事項を犠牲にしてパフォーマンスの最後のナノ秒を絞り出す必要がある」ようなものであれば、おそらくスタックオーバーフローで質問するのではなく、プロファイラーを解読する必要があります。)
これはほぼ確実に心配する必要はありませんが、パフォーマンスに大きな違いがあるかどうかは疑問です。違いが見られる唯一の状況は追い越し時です 大きい 値のタイプ。
他のヒント
より「科学的な」答えを得るために、私は自分でこれを確認することにしました。彼らも同じです。以下のコードを使用すると、ByVal は ByRef よりも約 2% 遅くなります。ただし、これらを入れ替えて、ByVal の前に ByRef のタイミングを設定すると、ByRef の方が約 2% 遅くなります。したがって、この場合、ByRef や ByVal よりも実際に重要なのは、それらが実行される順序です:)
Function CreateString()
Dim i As Integer
Dim str As String = ""
For i = 1 To 10000
str = str & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Next i
Return str
End Function
Sub fooval(ByVal Str As String)
Str = Str & "foobar"
End Sub
Sub fooref(ByRef Str As String)
Str = Str & "foobar"
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim str As String = CreateString()
Dim stopWatch As New Stopwatch
Dim i As Integer
stopWatch.Start()
For i = 1 To 1000
fooval(str)
Next i
stopWatch.Stop()
Dim valtime As Long = stopWatch.ElapsedMilliseconds
stopWatch.Restart()
For i = 1 To 1000
fooref(str)
Next i
stopWatch.Stop()
Dim reftime As Long = stopWatch.ElapsedMilliseconds
MsgBox("Val took " & valtime & " milliseconds and Ref took " & reftime & " milliseconds")
End Sub
文字列を含むクラス型の動作を理解するには、すべてのクラス型パラメータ、変数、フィールド、配列要素などを考慮してください。「オブジェクトID」を保持するものとして。もし Foo
型の変数です string
, 、ステートメント Foo = 12345.ToString();
新しいオブジェクト ID (仮に、オブジェクト ID#197) を作成し、次のタイプの新しいオブジェクトを作成します。 string
その ID で 5 文字を保持します "12345"
. 。その後、保存されます Object ID#197
変数に入れる Foo
. 。非 ref パラメータを指定してルーチンを呼び出した場合 param
, 、そして合格します Foo
それなら param
ローカル変数保持になります Object ID #197
. 。声明 param += "6";
新しいオブジェクトを作成します (例:オブジェクト ID #521)、文字列型、6 文字を保持 "123456"
そして保管する Object ID #521
の中へ param
. 。ご了承ください Foo
まだ保持しています Object ID#197
, 、そしてそのオブジェクトはまだ 5 文字の文字列を保持しています "12345"
.
もし param
通り過ぎていました ref
, 、次にステートメント param += "6"
保管していたでしょう Object ID #521
の中へ Foo
. 。おそらくガベージ コレクションの対象にすることを除いて、オブジェクト #197 に目に見える変化はまだ生じていないでしょう ( Foo
はオブジェクト #197 への唯一の参照であり、それを上書きすると、そのオブジェクトへの参照は宇宙のどこにも存在しなくなることを意味します)。
一般に、次のような不変クラス型について推論するのは非常に簡単であることに注意してください。 string
, オブジェクト ID について考えなくても、文字列変数によって表される文字のシーケンスを変更する唯一の方法は、そこに別のオブジェクト ID を格納することです。ただし、変更可能なクラス型を扱う場合は、オブジェクト ID の観点から考えることが不可欠になります。クラス型の変数を渡す Car
, 参照ではなく、VINをある紙片から別の紙片にコピーし、後者の紙片を何人かの店員に渡して、それを使って何かをするように頼むのと同じことになります。最初の書類で VIN#15934 の赤い車が特定されていた場合、作業員が作業を終えたときに最初の書類で VIN#15934 の青い車が特定される可能性がありますが、それは同じ車です。労働者が与えられた紙片に対してできることは何もなく、車に対してできることは何もなく、最初の紙がどの車について言及していたのかは変わりません。一方、パラメータを参照渡しすることは、店員に VIN が書かれた紙を渡し、作業が終わったらその紙を取り戻すことに似ています。作業員が VIN に取り消し線を引いて別の車を書くことができた場合、紙片を返却すると、それは同じ車または別の車を指している可能性があります。それが別の車について言及している場合、最初に言及した車は改造されている場合とされていない可能性があり、最終的に論文が言及している車は元の車と類似している場合もあれば、そうでない場合もあります。