c# - 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,6599 毫秒,例如,对于 List diag 中的 3000 条记录,它需要 3000*4,6599 = 14 秒。有没有优化代码的选项?
解决方案
您可以加快您强调的特定陈述
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;
}
想法是找到与您的,Datetime
相等或更大的第一条记录。如果找到完全匹配 - 它返回匹配元素的索引。如果未找到 - 它返回负值,可以通过操作将其转换为大于目标的第一个元素的索引。min
BinarySearch
~index
当我们找到那个元素时,我们可以向前移动列表并抓取项目,直到找到Datetime
大于最大值的项目(因为列表已排序)。我们还需要向后退一点,因为如果有重复 - 二分查找将不必返回第一个,所以我们需要向后退以寻找潜在的重复。
其他改进可能包括:
将活动代码放在for 循环之外的
Dictionary
(由 键控)中,从而将代码搜索替换为.Value
Where
Dictionary.ContainsKey
正如@Digitalsa1nt 评论中所建议的那样 - 使用
Parallel.For
、PLINQ 或任何类似技术并行化 foreach 循环。这是并行化的完美案例,因为循环仅包含 CPU 绑定的工作。当然,您需要进行一些调整以使其成为线程安全的,例如使用线程安全集合errors
(或锁定添加到它)。
推荐阅读
- javascript - 获取 ParseError: Identifier is expected on importing svelte files for testing with jest
- javascript - $(document).ready 中的代码,有时有效,有时无效
- vue.js - Vue内联点击事件`this`未定义
- c# - 没有内置功能的 ASP.NET 授权
- c - 为什么 gdb 在 RIP 相对模式下显示与绝对地址不同的地址?
- php - 如何在我的 Laravel 应用上发布图片?
- json - 如何快速将链接照片设置为按钮图像背景?
- php - 从 mysql 切换到 mysqli - 从同一数据库中的两个表中获取数据
- java - 为什么从 main 运行方法会导致“进程以退出代码 130 完成(被信号 2:SIGINT 中断)”
- python - 如何读取 csv 行并将其与我的列表进行比较