我正在创建一个报表生成工具,该工具使用来自我们系统的不同来源的自定义数据类型。用户可以创建报告模式,并根据询问的内容,根据不同的索引键,时间,时间范围等关联数据。项目不在关系数据库中进行查询,它是来自RAM的集合中的纯C#代码。 / p>
我遇到了一个巨大的性能问题,我几天后就在查看我的代码,并且正在努力优化它。
我将代码最小化了一个简短的例子,以便将探查器指向有问题的算法,但实际版本在更多条件和使用日期时会更复杂。
简而言之,此函数返回满足条件的“值”子集,具体取决于从“索引行”中选择的值的键。
private List<LoadedDataSource> GetAssociatedValues(IReadOnlyCollection<List<LoadedDataSource>> indexRows, List<LoadedDataSource> values)
{
var checkContainers = ((ValueColumn.LinkKeys & ReportLinkKeys.ContainerId) > 0 &&
values.Any(t => t.ContainerId.HasValue));
var checkEnterpriseId = ((ValueColumn.LinkKeys & ReportLinkKeys.EnterpriseId) > 0 &&
values.Any(t => t.EnterpriseId.HasValue));
var ret = new List<LoadedDataSource>();
foreach (var value in values)
{
var valid = true;
foreach (var index in indexRows)
{
// ContainerId
var indexConservedSource = index.AsEnumerable();
if (checkContainers && index.CheckContainer && value.ContainerId.HasValue)
{
indexConservedSource = indexConservedSource.Where(t => t.ContainerId.HasValue && t.ContainerId.Value == value.ContainerId.Value);
if (!indexConservedSource.Any())
{
valid = false;
break;
}
}
//EnterpriseId
if (checkEnterpriseId && index.CheckEnterpriseId && value.EnterpriseId.HasValue)
{
indexConservedSource = indexConservedSource.Where(t => t.EnterpriseId.HasValue && t.EnterpriseId.Value == value.EnterpriseId.Value);
if (!indexConservedSource.Any())
{
valid = false;
break;
}
}
}
if (valid)
ret.Add(value);
}
return ret;
}
这适用于小样本,但只要我有数千个值,2-3个索引行也有几十个值,生成可能需要数小时。
正如您所看到的,我会在索引条件失败后立即尝试中断并传递给下一个值。
我可以在一个单独的“values.Where(####)。ToList()”中做所有事情,但这种情况变得很复杂。
我尝试在indexConservedSource周围生成IQueryable,但情况更糟。我尝试使用带有ConcurrentBag的Parallel.ForEach进行“ret”,而且速度也慢了。
还能做些什么?
答案 0 :(得分:1)
原则上,你正在做的是计算两个序列的交集。你使用两个嵌套循环,因为时间是O(m * n),所以它很慢。您还有两个选择:
对于这种情况,第二种方法似乎更好。只需将这些索引列表转换为HashSet并对其进行测试。我添加了一些灵感代码:
private List<LoadedDataSource> GetAssociatedValues(IReadOnlyCollection<List<LoadedDataSource>> indexRows, List<LoadedDataSource> values)
{
var ret = values;
if ((ValueColumn.LinkKeys & ReportLinkKeys.ContainerId) > 0 &&
ret.Any(t => t.ContainerId.HasValue))
{
var indexes = indexRows
.Where(i => i.CheckContainer)
.Select(i => new HashSet<int>(i
.Where(h => h.ContainerId.HasValue)
.Select(h => h.ContainerId.Value)))
.ToList();
ret = ret.Where(v => v.ContainerId == null
|| indexes.All(i => i.Contains(v.ContainerId)))
.ToList();
}
if ((ValueColumn.LinkKeys & ReportLinkKeys.EnterpriseId) > 0 &&
ret.Any(t => t.EnterpriseId.HasValue))
{
var indexes = indexRows
.Where(i => i.CheckEnterpriseId)
.Select(i => new HashSet<int>(i
.Where(h => h.EnterpriseId.HasValue)
.Select(h => h.EnterpriseId.Value)))
.ToList();
ret = ret.Where(v => v.EnterpriseId == null
|| indexes.All(i => i.Contains(v.EnterpriseId)))
.ToList();
}
return ret;
}