"收益率回报"是否比"旧学校"回归慢?

Mar*_*mes 5 c# yield-return

我正在做一些关于收益率回归性能的测试,我发现它比正常回报慢.

我测试了值变量(int,double等)和一些引用类型(字符串等)......并且两种情况下的yield return都较慢.为什么要用呢?

看看我的例子:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jim*_*hel 12

考虑之间的差异File.ReadAllLinesFile.ReadLines.

ReadAllLines将所有行加载到内存中并返回一个string[].如果文件很小,一切都很好.如果文件大于适合内存的文件,则内存不足.

ReadLines另一方面,用于一次yield return返回一行.有了它,您可以阅读任何大小的文件.它不会将整个文件加载到内存中.

假设您要查找包含单词"foo"的第一行,然后退出.使用时ReadAllLines,即使第一行出现"foo",您也必须将整个文件读入内存.有了ReadLines,你只读了一行.哪一个会更快?

这不是唯一的原因.考虑一个读取文件并处理每一行的程序.使用File.ReadAllLines,您最终得到:

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

程序执行所花费的时间等于读取文件所需的时间,加上处理行的时间.想象一下,处理需要很长时间,以至于你想用多个线程来加速它.所以你做的事情如下:

lines = File.ReadAllLines(filename);
Parallel.Foreach(...);
Run Code Online (Sandbox Code Playgroud)

但阅读是单线程的.在主线程加载整个文件之前,您的多个线程无法启动.

有了ReadLines,不过,你可以这样做:

Parallel.Foreach(File.ReadLines(filename), line => { ProcessLine(line); });
Run Code Online (Sandbox Code Playgroud)

这会立即启动多个线程,这些线程在读取其他线路的同时进行处理.因此,读取时间与处理时间重叠,这意味着您的程序将更快地执行.

我使用文件展示我的示例,因为它更容易以这种方式演示概念,但对于内存中的集合也是如此.使用yield return将使用较少的内存,并可能更快,称只需要看看集合的一部分(尤其是方法的时候Enumerable.Any,Enumerable.First等).

  • @MichelAlmeida:不。底层流以 4 KB 或更多的块加载文件,然后从中解析行。所以磁盘读取的次数被最小化了。此外,操作系统通常启用某种类型的预读缓存,因此当流请求更多信息时,它已经在内存中,而“读取”只是将数据从一个内存位置复制到另一个内存位置。 (2认同)