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

C#-Linq使用List和Where子句优化代码,c#,performance,linq,optimization,C#,Performance,Linq,Optimization,我有以下代码: var tempResults = new Dictionary<Record, List<Record>>(); errors = new List<Record>(); foreach (Record record in diag) { var code = Convert.ToInt16(Regex.Split(record.L

我有以下代码:

        var tempResults = new Dictionary<Record, List<Record>>();            
        errors = new List<Record>();
        foreach (Record record in diag)
        {
            var code = Convert.ToInt16(Regex.Split(record.Line, @"\s{1,}")[4], 16);                
            var cond = codes.Where(x => x.Value == code && x.Active).FirstOrDefault();
            if (cond == null)
            {
                errors.Add(record);
                continue;
            }

            var min = record.Datetime.AddSeconds(downDiff);
            var max = record.Datetime.AddSeconds(upDiff);

            //PROBLEM PART - It takes around 4,5ms
            var possibleResults = cas.Where(x => x.Datetime >= min && x.Datetime <= max).ToList();

            if (possibleResults.Count == 0)
                errors.Add(record);
            else
            {                    
                if (!CompareCond(record, possibleResults, cond, ref tempResults, false))
                {                        
                        errors.Add(record);
                }
            }
        }
var tempResults=new Dictionary();
错误=新列表();
foreach(在diag中记录)
{
var code=Convert.ToInt16(Regex.Split(record.Line,@“\s{1,}”)[4],16);
var cond=code.Where(x=>x.Value==code&&x.Active).FirstOrDefault();
如果(cond==null)
{
错误。添加(记录);
继续;
}
var min=记录.Datetime.AddSeconds(downDiff);
var max=记录.Datetime.AddSeconds(upDiff);
//问题部分-大约需要4,5毫秒
var possibleResults=cas.Where(x=>x.Datetime>=min&&x.Datetime尝试在列表中添加

AsNoTracking
方法可以节省执行时间和内存使用量。当我们从数据库检索大量数据时,应用此选项变得非常重要

var possibleResults = cas.Where(x => x.Datetime >= min && x.Datetime <= max).AsNoTracking().ToList(); //around 4,6599ms

var possibleResults=cas.Where(x=>x.Datetime>=min&&x.Datetime您可以在这里进行一些改进。
这可能只是一个小的性能提升,但在这种情况下,您应该尝试使用groupby,而不是where

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

cas.GroupBy(x => x.DateTime >= min && x.DateTime <= max).Select(h => h.Key == true);
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;
}    
cas.GroupBy(x=>x.DateTime>=min&&x.DateTime h.Key==true);
这通常适用于搜索不同值的列表,但在您的情况下,我不确定它在使用子句时是否能为您提供任何好处

在整个代码中,您还可以做一些其他事情:

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

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

cas.Where(x => x.Datetime >= min && x.Datetime <= max).ToList();
然后为
记录
创建比较器,该比较器将只比较
日期时间
属性(实现假定列表中没有空记录):

想法是使用
BinarySearch
,查找日期时间等于或大于
min
的第一条记录。如果找到精确匹配,则返回匹配元素的索引。如果未找到,则返回负值,可通过
~index
操作转换为大于目标的第一个元素的索引

当我们找到该元素时,我们可以向前移动列表并抓取项目,直到找到
Datetime
大于max的项目(因为列表已排序).我们还需要向后退一步,因为如果有重复项,二进制搜索将不必返回第一个,因此我们需要向后退一步寻找潜在的重复项

其他改进可能包括:

  • 将活动代码放入for循环外部的
    字典中(由
    值键入),从而用
    字典代替
    中的
    搜索

  • 正如@Digitalsa1nt-parallelize foreach循环的评论中所建议的,使用
    并行。对于
    、PLINQ或任何类似技术。这是并行化的完美案例,因为循环只包含CPU限制的工作。当然,您需要做一些调整以使其线程安全,例如对
    er使用线程安全集合ROR
    (或锁定添加到其中)


您可以在forEach循环外部对
代码进行预筛选,使其仅包含活动的项目。这将减少您必须在循环内部搜索的项目数。是的,这是事实,谢谢您的通知。我已经这样做了。现在,它稍微好一点,但主要问题仍然是where子句随时间的推移。不幸的是,它没有效果。谢谢!太棒了。4,5毫秒减少到了0,5毫秒左右,效果好多了。这是我第一次听说BinarySearch,很高兴了解它。:@Filiprocházka,这足够快了,或者你想要更快吗?:)这就足够了。:)但是如果有另外一个选项来优化它,并且你有时间来描述/解释如何做,我喜欢学习。:)或者告诉我应该搜索什么关键字,我会尝试自己学习/做。@Filiprocházka我感觉它可以更快,但不能仅仅用代码提供更多指导,而没有这个目标t代码。但您可以将活动代码移出循环并放入
HashSet
。如果您有很多代码,它可能会加快一些速度。但除此之外,我没有更多的想法。@Digitalsa1nt是的,实际上这是并行化的完美案例。几乎没有可变共享状态和CPU限制的工作。
private class RecordDateComparer : IComparer<Record> {
    public int Compare(Record x, Record y) {
        return x.Datetime.CompareTo(y.Datetime);
    }
}
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;
}