C# - Linq使用List和Where子句优化代码

时间:2018-05-04 08:35:27

标签: c# performance linq optimization

我有以下代码:

var array1 = [1, 2, 3, 4];
var array2 = [5, 6, 7, 8, 100];
var sum = array1.map((num, idx) => num + array2[idx]); // [6,8,10,12]

变量diag是记录列表

变量cas是具有大约50k项目的记录列表。

问题在于它太慢了。具有第一个where子句的部分需要大约4,6599ms,例如对于列表诊断中的3000条记录,它使得3000 * 4,6599 = 14秒。有优化代码的选项吗?

3 个答案:

答案 0 :(得分:6)

您可以加快您强调的具体陈述

cas.Where(x => x.Datetime >= min && x.Datetime <= max).ToList();

通过cas列表进行二进制搜索。首先按cas预先排序Datetime

cas.Sort((a,b) => a.Datetime.CompareTo(b.Datetime));

然后为Record创建比较器,它只比较Datetime属性(实现假定列表中没有空记录):

private class RecordDateComparer : IComparer<Record> {
    public int Compare(Record x, Record y) {
        return x.Datetime.CompareTo(y.Datetime);
    }
}

然后你可以翻译你的Where条款:

var index = cas.BinarySearch(new Record { Datetime = min }, new RecordDateComparer());
if (index < 0)
    index = ~index;
var possibleResults = new List<Record>();    
// go backwards, for duplicates            
for (int i = index - 1; i >= 0; i--) {
    var res = cas[i];
    if (res.Datetime <= max && res.Datetime >= min)
        possibleResults.Add(res);
    else break;
}
// go forward until item bigger than max is found
for (int i = index; i < cas.Count; i++) {
    var res = cas[i];
    if (res.Datetime <= max &&res.Datetime >= min)
        possibleResults.Add(res);
    else break;
}    

想法是找到Datetimemin相等或更高的第一条记录,BinarySearch。如果找到完全匹配 - 它返回匹配元素的索引。如果未找到 - 它返回负值,可以使用~index操作将其转换为大于目标的第一个元素的索引。

当我们找到该元素时,我们可以直接前进列表并抓取项目,直到找到Datetime大于max的项(因为列表已排序)。我们还需要稍微退一步,因为如果有重复 - 二元搜索不需要返回第一个,所以我们需要向后搜索可能的重复项。

其他改进可能包括:

  • 将活动代码置于for循环之外的Dictionary(由Value键控),从而将代码Where替换为Dictionary.ContainsKey

  • 正如@ Digitalsa1nt的评论中所建议的那样 - 使用Parallel.For,PLINQ或任何类似技术并行化foreach循环。这是并行化的完美案例,因为循环只包含CPU绑定工作。您需要进行一些调整以使其成为线程安全的,例如使用errors的线程安全集合(或锁定添加它)。

答案 1 :(得分:0)

尝试在列表中添加AsNoTracking

$request->all();方法可以节省执行时间和内存使用量。当我们从数据库中检索大量数据时,应用此选项确实很重要。

if($res['member_id']){
    Academico::where('member_id', $res['member_id'])->delete();
}

答案 2 :(得分:0)

您可以在此处进行一些改进。 它可能只是一个小的性能提升,但您应该尝试使用groupby而不是在这种情况下的位置。

所以你应该有这样的东西:

cas.GroupBy(x => x.DateTime >= min && x.DateTime <= max).Select(h => h.Key == true);

这通常适用于通过列表查找不同的值,但在您的情况下,我不确定它在使用子句时是否会为您提供任何好处。

您可以在整个代码中执行其他一些操作:

  • 尽可能避免使用ToList并坚持使用IEnumerable。 ToList执行急切的评估,这可能会导致查询速度大幅下降。
  • 在检查值是否存在时使用.Any()而不是Count(仅当列表为IEnumerable时才适用)