我有以下代码: 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绑定工作.您需要进行一些调整以使其成为线程安全的,例如使用线程安全的收集来处理错误(或锁定添加它).