5

利回りリターンのパフォーマンスについていくつかのテストを行っていますが、通常のリターンよりも遅いことがわかりました。

値変数 (int、double など) といくつかの参照型 (string など) をテストしました...どちらの場合も、yield の戻り値が遅くなりました。なぜそれを使うのですか?

私の例をチェックしてください:

public class YieldReturnTeste
{
    private static IEnumerable<string> YieldReturnTest(int limite)
    {
        for (int i = 0; i < limite; i++)
        {
            yield return i.ToString();
        }
    }

    private static IEnumerable<string> NormalReturnTest(int limite)
    {
        List<string> listaInteiros = new List<string>();

        for (int i = 0; i < limite; i++)
        {
            listaInteiros.Add(i.ToString());
        }
        return listaInteiros;
    }

    public static void executaTeste()
    {
        Stopwatch stopWatch = new Stopwatch();

        stopWatch.Start();

        List<string> minhaListaYield = YieldReturnTest(2000000).ToList();

        stopWatch.Stop();

        TimeSpan ts = stopWatch.Elapsed;


        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",

        ts.Hours, ts.Minutes, ts.Seconds,

        ts.Milliseconds / 10);

        Console.WriteLine("Yield return: {0}", elapsedTime);

        //****

        stopWatch = new Stopwatch();

        stopWatch.Start();

        List<string> minhaListaNormal = NormalReturnTest(2000000).ToList();

        stopWatch.Stop();

        ts = stopWatch.Elapsed;


        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",

        ts.Hours, ts.Minutes, ts.Seconds,

        ts.Milliseconds / 10);

        Console.WriteLine("Normal return: {0}", elapsedTime);
    }
}
4

5 に答える 5

14

と の違いを考えてみましょFile.ReadAllLinesFile.ReadLines

ReadAllLinesすべての行をメモリにロードし、 を返しますstring[]。ファイルが小さい場合は、すべて問題ありません。ファイルがメモリに収まらないほど大きい場合、メモリが不足します。

ReadLines一方、 を使用yield returnして一度に 1 行ずつ返します。これにより、任意のサイズのファイルを読み取ることができます。ファイル全体をメモリにロードするわけではありません。

「foo」という単語を含む最初の行を見つけて終了したいとします。を使用ReadAllLinesすると、最初の行に「foo」が発生した場合でも、ファイル全体をメモリに読み込む必要があります。ではReadLines、1 行だけを読み取ります。どちらが速いでしょうか?

それだけが理由ではありません。ファイルを読み取り、各行を処理するプログラムを考えてみましょう。を使用するFile.ReadAllLinesと、次のようになります。

string[] lines = File.ReadAllLines(filename);
for (int i = 0; i < lines.Length; ++i)
{
    // process line
}

そのプログラムの実行にかかる時間は、ファイルの読み取りにかかる時間と、行の処理にかかる時間に等しいです。処理に時間がかかりすぎて、複数のスレッドで処理を高速化したい場合を想像してください。だからあなたは次のようなことをします:

lines = File.ReadAllLines(filename);
Parallel.Foreach(...);

ただし、読み取りはシングルスレッドです。メインスレッドがファイル全体をロードするまで、複数のスレッドを開始できません。

ただし、を使用ReadLinesすると、次のようなことができます。

Parallel.Foreach(File.ReadLines(filename), line => { ProcessLine(line); });

これにより、複数のスレッドがすぐに起動し、他の行が読み取られると同時に処理されます。したがって、読み取り時間は処理時間とオーバーラップします。つまり、プログラムの実行速度が速くなります。

ファイルを使用した例を示したのは、ファイルを使用した方が概念を説明しやすいからですが、同じことがメモリ内コレクションにも当てはまります。を使用すると、特にコレクションの一部 ( 、など)yield returnのみを参照する必要があるメソッドを呼び出す場合に、使用するメモリが少なくなり、高速になる可能性があります。Enumerable.AnyEnumerable.First

于 2013-08-09T12:29:24.000 に答える
2

一つには、これは便利な機能です。2 つ目は、値がフェッチされたときにのみ評価されることを意味する、遅延リターンを実行できることです。これは、DB クエリや、完全に反復したくないコレクションのようなものでは非常に貴重です。3 つ目は、シナリオによっては高速になる可能性があることです。4、違いは何ですか?おそらく小さいので、マイクロ最適化。

于 2013-08-09T11:49:06.630 に答える
1

C# コンパイラはイテレータ ブロック ( yield return) をステート マシンに変換するためです。この場合、ステート マシンは非常に高価です。

ここで詳細を読むことができます: http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx

于 2013-08-09T12:14:49.740 に答える
0

yield return を使用して、アルゴリズムから結果を取得しました。すべての結果は前の結果に基づいていますが、すべてが必要というわけではありません。foreach と yield return を使用して各結果を検査し、要件を満たす結果が得られた場合は foreach ループを中断しました。

アルゴリズムはかなり複雑だったので、それぞれの歩留りリターンの間に状態を保存するための適切な作業が含まれていたと思います。

従来のリターンよりも 3% ~ 5% 遅いことに気付きましたが、すべての結果を生成する必要がないことから得られる改善は、パフォーマンスの低下よりもはるかに大きなものです。

于 2013-11-07T20:33:58.423 に答える
0

それ以外の.ToList()場合は IEnumerable の延期された反復を実際に完了するために必要ですが、コア部分の測定を妨げます。

少なくとも、リストを既知のサイズに初期化することが重要です。

const int listSize=2000000; var tempList = 新しいリスト (リストサイズ);

...

List tempList = YieldReturnTest(listSize).ToList();

備考: 私のマシンでは両方の呼び出しにほぼ同じ時間がかかりました. 違いはありません (repl.it の Mono 4)。

于 2017-05-21T20:38:27.070 に答える