当前位置 : 主页 > 编程语言 > c语言 >

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

来源:互联网 收集:自由互联 发布时间:2021-06-25
我有以下代码: var tempResults = new DictionaryRecord, ListRecord(); errors = new ListRecord(); foreach (Record record in diag) { var code = Convert.ToInt16(Regex.Split(record.Line, @"\s{1,}")[4], 16); var cond = codes.Where(x = x.Va
我有以下代码:

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);
                }
            }
        }

变量diag是记录列表

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

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

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

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

使用二进制搜索cas list.按日期时间首先预排序cas:

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;
}

想法是使用BinarySearch找到日期时间等于或大于min的第一条记录.如果找到完全匹配 – 它返回匹配元素的索引.如果未找到 – 它返回负值,可以使用~sdex操作将其转换为大于目标的第一个元素的索引.

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

其他改进可能包括:

>将活动代码放在for循环之外的Dictionary(由Value键控)中,从而替换使用Dictionary.ContainsKey搜索的代码.>正如@ Digitalsa1nt的评论中所建议的那样 – 并行化foreach循环,使用Parallel.For,PLINQ或任何类似的技术.这是并行化的完美案例,因为循环只包含CPU绑定工作.您需要进行一些调整以使其成为线程安全的,例如使用线程安全的收集来处理错误(或锁定添加它).

网友评论