14

こんにちは、これは本当に私を悩ませているものであり、誰かが私に答えてくれることを願っています。私はref(とout)について読んでいて、sを使用してコードの速度を落としているかどうかを調べようとしていますref。通常、私は次のようなものを置き換えます。

int AddToInt(int original, int add){ return original+add; }

void AddToInt(ref int original, int add){ original+=add; } // 1st parameter gets the result

私の目にはこれ

AddToInt(ref _value, _add);

これよりも読みやすく、コードも簡単です

_value = AddToInt(_value, _add);

ref値を返すのではなく、を使用してコードで何をしているのかを正確に把握しています。ただし、パフォーマンスは私が真剣に受け止めているものであり、参照を使用すると、間接参照とクリーンアップが大幅に遅くなるようです。

私が知りたいのは、私が読んだすべての投稿refで、ref例がより小さく、よりクリーンで、より正確です。

また、値型を返すよりも実際に遅い理由を知りたいと思いrefます。関数値を返す前に多くの編集を行う場合、実際の変数を参照する方が速いように思われます。メモリからクリーンアップされる直前の変数のインスタンスとは対照的に、それを編集します。

4

6 に答える 6

13

「ref」がパフォーマンスと同じ文で使用される主な場合は、非常に非典型的なケースについて説明する場合です。たとえば、ゲームの「オブジェクト」がクラスではなく構造体で表されるXNAシナリオでは、GCの問題を回避します。 XNAに不釣り合いな影響を及ぼします)。これは次の場合に役立ちます。

  • 特大の構造体をスタックに複数回コピーしないようにする
  • 構造体コピーの変更によるデータ損失を防止します(XNA構造体は通常、通常の慣行に反して変更可能です)
  • 構造体をコピーして元に戻すのではなく、配列内の構造体を直接渡すことができます

他のすべての場合、「ref」はより一般的に追加の副作用に関連付けられ、戻り値で簡単に表現することはできません(たとえばを参照Monitor.TryEnter)。

XNA / structのようなシナリオがなく、厄介な副作用がない場合は、戻り値を使用してください。より一般的である(それ自体に価値がある)ことに加えて、渡すデータが少なくなり(たとえば、intはx64のrefよりも小さい)、必要な間接参照が少なくなる可能性があります。

最後に、リターンアプローチはより用途が広いです。ソースを常に更新する必要はありません。対比:

// want to accumulate, no ref
x = Add(x, 5);

// want to accumulate, ref
Add(ref x, 5);

// no accumulate, no ref
y = Add(x, 5);

// no accumulate, ref
y = x;
Add(ref y, x);

最後のものは最も明確ではないと思います(他の「ref」はその後ろにあります)。refの使用法は、明示的でない言語(VBなど)ではさらに明確ではありません。

于 2011-11-13T11:00:12.263 に答える
6

refキーワードを使用する主な目的は、変数の値が渡される関数によって変更できることを示すことです。変数を値で渡す場合、関数内からの更新は元のコピーに影響しません。

複数の戻り値が必要で、戻り値用の特別な構造体またはクラスを構築するのはやり過ぎになる状況で非常に便利です(そしてより高速です)。例えば、

public void Quaternion.GetRollPitchYaw(ref double roll, ref double pitch, ref double yaw){
    roll = something;
    pitch = something;
    yaw = something;
}

これは、ポインターの使用が制限されていない言語のかなり基本的なパターンです。c / c ++では、クラスと配列をポインターとして、プリミティブが値によって渡されることがよくあります。C#は正反対のことをするので、上記のような状況では「ref」が便利です。

更新する変数をrefによって関数に渡す場合、結果を得るのに必要な書き込み操作は1回だけです。ただし、値を返すときは、通常、関数内の変数に書き込み、それを返し、宛先変数に再度書き込みます。データによっては、これにより不要なオーバーヘッドが追加される可能性があります。とにかく、これらはrefキーワードを使用する前に私が通常考慮する主なことです。

refは、c#でこのように使用すると少し速くなることがありますが、パフォーマンスの正当化として使用するには不十分です。

これは、7年前のマシンで、以下のコードを使用して、参照と値で100kの文字列を渡して更新したものです。

出力:

反復:10000000 byref:165ms byval:417ms

private void m_btnTest_Click(object sender, EventArgs e) {

    Stopwatch sw = new Stopwatch();

    string s = "";
    string value = new string ('x', 100000);    // 100k string
    int iterations = 10000000;

    //-----------------------------------------------------
    // Update by ref
    //-----------------------------------------------------
    sw.Start();
    for (var n = 0; n < iterations; n++) {
        SetStringValue(ref s, ref value);
    }
    sw.Stop();
    long proc1 = sw.ElapsedMilliseconds;

    sw.Reset();

    //-----------------------------------------------------
    // Update by value
    //-----------------------------------------------------
    sw.Start();
    for (var n = 0; n < iterations; n++) {
        s = SetStringValue(s, value);
    }
    sw.Stop();
    long proc2 = sw.ElapsedMilliseconds;

    //-----------------------------------------------------
    Console.WriteLine("iterations: {0} \nbyref: {1}ms \nbyval: {2}ms", iterations, proc1, proc2);
}

public string SetStringValue(string input, string value) {
    input = value;
    return input;
}

public void SetStringValue(ref string input, ref string value) {
    input = value;
}
于 2013-11-26T05:20:01.753 に答える
4

ここでOndrejに同意する必要があります。文体の観点から、すべてを渡し始めると、ref最終的には、このようなAPIを設計するために首を絞めたい開発者と協力することになります。

メソッドからのものを返すだけで、メソッドの100%が返されることはありませんvoid。あなたがしていることは非常に汚れたコードにつながり、あなたのコードで作業することになった他の開発者を混乱させるかもしれません。とにかくオプトマイゼーションではあまり得られないので、ここではパフォーマンスよりも明確さを優先してください。

このSO投稿を確認してください:C#'ref'キーワード、パフォーマンス

とJonSkeetからのこの記事:http ://www.yoda.arachsys.com/csharp/parameters.html

于 2011-11-13T10:51:54.693 に答える
1

まず、使用が遅いか速いかを気にしないでください。refそれは時期尚早の最適化です。99.9999%の場合、これがパフォーマンスのボトルネックを引き起こす状況に遭遇することはありません。

ref第2に、 Cに似た言語の通常の「機能的」な性質のため、使用ではなく戻り値として計算結果を返すことが推奨されます。これにより、ステートメント/呼び出しのチェーンが改善されます。

于 2011-11-13T10:46:13.010 に答える
0

あなたの場合、refを使用することは悪い考えです。
refを使用する場合、プログラムは、ポインターが指している値を読み取るよりも、スタックからポインターを読み取ります。
ただし、値を渡す場合は、スタックから値を読み取るだけで済み、基本的に読み取り量が半分になります。

参照による受け渡しは、3Dモデルや配列などの中規模から大規模のデータ構造にのみ使用する必要があります。

于 2020-03-11T13:53:44.457 に答える
0

基本的なデータ型にrefを使用することはお勧めできません。特に、コード行が少ない単純なメソッドの場合。まず、C#コンパイラは、コードを高速化するために多くの最適化を行います。私のベンチマークによると、https: //rextester.com/CQJR12339 refを渡すと、パフォーマンスが低下します。参照が渡されるとき、ポインタ変数として8バイトをコピーしています(64ビットプロセッサを想定)。8バイトのdoubleを直接渡してみませんか?

Pass by refは、大きなオブジェクトの場合に役立ちます。たとえば、文字数の多い文字列などです。

于 2020-05-07T10:06:05.783 に答える