C# – Linq使用List和Where子句优化代码
我有以下代码:
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绑定工作.您需要进行一些调整以使其成为线程安全的,例如使用线程安全的收集来处理错误(或锁定添加它). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |