是';收益率回报率;慢于“;老派;回来
本文关键字:老派 回来 慢于 收益率 回报率 | 更新日期: 2023-09-27 18:27:05
我正在做一些关于收益回报性能的测试,发现它比正常回报慢。
我测试了值变量(int、double等)和一些引用类型(string等)……在这两种情况下,收益率都较慢。那为什么要用它呢?
看看我的例子:
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);
}
}
考虑File.ReadAllLines
和File.ReadLines
之间的差异。
CCD_ 3将所有线路加载到存储器中并返回一个CCD_。如果文件很小,一切都很好。如果文件的大小超过了内存容量,那么内存就会用完。
另一方面,ReadLines
使用yield return
一次返回一行。使用它,您可以读取任何大小的文件。它不会将整个文件加载到内存中。
假设您想找到包含单词"foo"的第一行,然后退出。使用ReadAllLines
,即使第一行出现"foo",也必须将整个文件读入内存。使用ReadLines
,您只能读取一行。哪一个更快?
这不是唯一的原因。考虑一个读取文件并处理每一行的程序。使用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.Any
、Enumerable.First
等)的方法时。
首先,这是一个方便的特性。第二,它允许您进行惰性返回,这意味着只有在获取值时才会对其进行求值。对于DB查询,或者只是一个不想完全迭代的集合,这可能是非常宝贵的。第三,在某些情况下可能会更快。第四,有什么不同?可能很小,所以微观优化。
yield return
)转换为状态机。在这种情况下,状态机非常昂贵。
您可以在此处阅读更多信息:http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx
我使用yield return来给出算法的结果。每个结果都是基于以前的结果,但我不需要所有的结果。我使用带有yield return的foreach来检查每个结果,如果得到满足要求的结果,则中断foreach循环。
该算法相当复杂,所以我认为在每次收益回报之间保存状态需要做一些不错的工作。
我注意到它比传统的回报慢3%-5%,但我在不需要产生所有结果的情况下获得的状态改善比性能损失要大得多。
.ToList()
虽然对于真正完成IEnumerable的延迟迭代是必要的,但它阻碍了对核心部分的测量。
至少将列表初始化为已知大小是很重要的:
const int listSize=2000000;var tempList=新列表(listSize);
List-tempList=YieldReturnTest(listSize).ToList();
备注:在我的机器上,两个电话花的时间差不多。。没有差异(在repl.it上的Mono 4)。