private void GenerateRecords(JobRequest request)
{
for (var day = 0; day < daysInRange; day++)
{
foreach (var coreId in request.CoreIds)
{
foreach (var agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid()))
{
for (var copiesDone = 0; copiesDone < request.CopiesToMake; copiesDone++)
{
foreach (var jobInfoRecord in request.Jobs)
{
foreach (var status in request.Statuses)
{
//DoSomeWork();
}
}
}
}
}
}
}
有没有办法提高多次迭代的性能?我真的需要拥有所有这些循环,但我想知道如何改进(加速)迭代?也许使用linq?
答案 0 :(得分:4)
一旦你放弃了LINQ,你就会受到收集枚举器和垃圾收集器的支配。
如果要从foreach
循环中尽可能多地挤出性能并控制数据结构,请确保使用具有结构枚举器的集合类型(即List<T>
,{ {1}})。更好的是,尽可能使用普通的通用数组。尽管是非结构枚举器,但它们在原始访问速度方面是.NET中最快的集合类型,至少在Release /中构建时启用了优化(在这种情况下,编译器为您的{{1}发出相同的IL }}循环遍历数组,就像ImmutableArray<T>
循环一样,从而减少通常与使用实现foreach
的类型相关联的分配和方法调用。)
Roslyn有set of guidelines个热门代码路径,在您的情况下非常有用:
- 避免使用LINQ。
- 避免在没有结构枚举器的集合上使用foreach。
现在,上述内容在任何性能关键情况下都有效。但是,我有点怀疑收集迭代性能是您特定情况下的瓶颈。 for
花费的时间比你想要的要长得多。您应该对IEnumerable<T>
方法调用进行概要分析,以获得关于哪些代码需要最多关注的明确答案。
如果您认为DoSomeWork
实施是最佳的,请考虑并行化您的工作量。
如果您的GenerateRecords
实现是纯粹的并且不依赖于外部可变状态(即类变量),那么您可以通过DoSomeWork
或{{1}并行化一些循环迭代。 }。你最外面的循环看起来像是一个特别好的候选者,但你可能不得不放置并行循环,直到你获得所需的性能特征。作为一个起点,我建议:
DoSomeWork
答案 1 :(得分:1)
您需要做多少次//DoSomeWork()
?
这些浪费了吗?
如果没有,那么循环并不重要。
您可以说,在Visual Studio IDE下运行它,并且在它运行时,点击&#34; Pause&#34;按钮。 然后显示调用堆栈。
除非//DoSomeWork()
少于几条指令,否则暂停将落在//DoSomeWork()
。
如果这样做10次,那么落在该函数中的样本分数大约是它花费的时间的一小部分。
如果10个样本中有8个落在该函数中,并且其中2个落在循环中(很可能是最里面的循环),那么即使您展开循环或将其成本降低到0,您也不会节省超过约20%。
花费精力的是最重要的成本。
答案 2 :(得分:1)
与旧学校相比,LINQ确实没有加速任何事情。 for循环。相反,LINQ通常成本很低。但是,根据您的问题(您应该测量以更好地理解它),您可以通过并行化解决方案来提高性能。在这里LINQ可以很方便。
您可以将所有for循环更改为使用LINQ创建的单个IEnumerable<T>
:
var query = from day in Enumerable.Range(0, request.daysInRange)
from coreId in request.CoreIds
from agentId in Enumerable.Range(0, request.AgentsCount).Select(x => Guid.NewGuid())
from copiesDone in Enumerable.Range(0, request.CopiesToMake)
from jobInfoRecord in request.Jobs
from status in request.Statuses
select new {
Day = day,
CoreId = coreId,
AgentId = agentId,
CopiesDone = copiesDone,
JobInfoRecord = jobInfoRecord,
Status = status
};
假设你在循环中所做的工作是创建一个新对象,你可以使用Select
来投射项目:
var results = query.Select(item => /* do some work and return something */);
但是,您可以使用PLINQ来并行化代码,并在插入AsParallel()
时略有变化:
var results = query.AsParallel().Select(item => /* do some work and return something */);
如果你有一个4核计算器,只要正在完成的工作是CPU限制的,你就可以期望速度提高近4倍。
调试并行代码可能很困难,但假设您调试的任何问题与并行执行的代码无关,您可以通过删除.AsParallel()
轻松关闭并行化。这可能非常方便。