一些快速背景 - 我编写了一些代码,根据Permutations, Combinations, and Variations using C# Generics
中的优秀(并且似乎是高效的)组合库生成项目组合如果你用最简单的形式写出我想要做的事情,那就是:
var items = dataService.GetAllItems();
PreFilters.ForEach(filter => items = filter.ApplyFilter(items));
var combinations = from item1 in items
from item2 in items
from item3 in items
from item4 in items
select new SpecialContainer(new List<Item>(){item1, item2, item3, item4});
combinations = combinations.Distinct().ToList();
PostFilters.ForEach(filter => combinations = filter.ApplyFilter(combinations));
...其中SpecialContainer
可以根据Id
中的Item
字段计算组合的唯一键(按字面顺序排序Id
并连接成{{} 1 {} string
}。
虽然4组合的表现并不差(280k项目,实际240ms),但一旦我们达到8组合(896mil,16mins投射),事情就会慢下来。理想情况下我也喜欢做12的组合,但考虑到有一个巨大的项目从4跳到8,从8跳到12可能意味着按顺序计算多年的时间。
我根据其他标准过滤这些组合,这有助于在组合过程之前降低组合的复杂性/数量(事先根据已知标准取消资格),并且在整个列表被列举后如代码示例所示。< / p>
所以我的问题:
Id1:Id2:Id3:Id4
,并仅在枚举时生成组合。无论如何,我可以利用IEnumerable<T>
处理,同时使用上面显示的AsParallel
类枚举和丢弃不相关的,非唯一的组合? Filter
类内部使用'ToList()'在每个阶段将Filter
转换为IEnumerable<T>
- 我是否应该将它们保留为List<T>
直到最后?更新
产生问题的实际调用如下:
IEnumerable<T>
上述代码中的var combinator = new Combinations<Item>(items, 8, GenerateOption.WithRepetition);
var combinations = combinator.Select(x => new SpecialContainer(x)).ToList();
是花费时间的调用。
更新2:
昨晚,我确保所有ToList()
项都没有在PostFilter
实施中调用ToList()
,而ApplyFilter
调用也没有“纯粹”{{1}在我将它绑定到UI之前,直到最后一次调用。我还在设置流程之前添加了额外的combinations.Distinct()
。
所以新代码如下:
IEnumerable<T>
这背后的原因是允许编译器潜在地优化生成的AsParallel()
操作堆栈。昨天晚上10点我把它踢掉了,虽然工作肯定是在不同的线程上进行的,但整个过程还没有在今天早上6点完成(之前的性能大约是16分钟)。
但是,阅读关于LINQ Performance和how PLINQ can affect performance的其他一些文章,我认为尝试在并行编程模式中优化这一点的下一步是更改我的过滤器以针对返回的单个对象运行一个var combinations = combinator.Select(x => new SpecialContainer(x)));
combinations = combinations.Distinct().AsParallel();
PostFilters.ForEach(filter => combinations = filter.ApplyFilter(combinations));
var finalList = combinations.ToList();
,所以我的代码最终会像:
IEnumerable<T>