简介:我花了一整天的时间来研究为什么我的处理操作如此之慢。低数据真的很慢。我检查了sql视图,程序和linq逻辑 - 所有这些都很完美。但后来我发现这个小东西需要很长时间来处理。
member X.CountStatistics()=
linq.TrueIncidents
|> PSeq.groupBy (fun v -> v.Name)
|> PSeq.map (fun (k, vs) -> k, PSeq.length vs)
|> Array.ofSeq
它只是计算分组值,但花费了多少时间!简易桌上约10秒钟,
必须有一些生气的递归,但我看不到它......
如何让这项操作“快一点”或将其重新编码为linq-to-sql?
答案 0 :(得分:4)
如果我理解正确,TrueIncidents是数据库中的一个表,您将整个内容拉入客户端应用程序以进行一些分组和计数。如果TrueIncidents是一个大表,那么这个操作总是会变慢,因为你正在移动大量的数据。执行此操作的“正确”方法是在数据库上,因为建议使用linq to SQL,或者Tomas建议使用存储过程。
关于PSeq,我不认为内联会产生很大的影响。并行化有一个开销,为了分摊这个开销,列表需要相对较大,并且您对列表中每个项目执行的操作需要很大。如果您对每个项目执行的操作非常昂贵,那么并行化可能对于小列表是值得的,但是相反的情况似乎是正确的;即使列表非常大并行化一个小的操作也不值得开销。因此,在这种情况下的问题是您对列表中的每个项目执行的操作太小,因此并行化的成本将始终使操作变慢。为了看到这一点,考虑下面的C#程序我们在一个包含1000万个项目的列表上执行一个简单的添加,你会发现并行版本总是运行缓慢(好吧,在我正在处理的机器上,这个两个内核,我想在具有更多内核的机器上,结果可能会有所不同)。
static void Main(string[] args)
{
var list = new List<int>();
for (int i = 0; i < 10000000; i++)
{
list.Add(i);
}
var stopwatch = new Stopwatch();
stopwatch.Start();
var res1 = list.Select(x => x + 1);
foreach (var i in res1)
{
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);
// 00:00:00.1950918 sec on my machine
stopwatch.Start();
var res2 = list.Select(x => x + 1).AsParallel();
foreach (var i in res2)
{
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);
// 00:00:00.3748103 sec on my machine
}
答案 1 :(得分:3)
当前版本的F#LINQ支持有点限制。
我认为写这个的最好方法是牺牲一些使用F#的优雅,并将其作为SQL中的存储过程编写。然后,您可以将存储过程添加到linq
数据上下文中,并使用生成的方法很好地调用它。当F#LINQ将来有所改进时,你可以改回来: - )。
关于PSeq
示例 - 据我所知,存在一些效率问题,因为这些方法没有内联(由于内联,编译器能够进行一些额外的优化,并且它消除了一些开销)。您可以尝试下载源代码并将inline
添加到map
和groupBy
。
答案 2 :(得分:0)
正如在其他答案中已经提到的那样,如果从数据库中提取大量数据然后对这个大型数据集进行一些计算,那么这将是非常昂贵的(我认为IO部分将比计算部分更昂贵)。在您的特定情况下,您似乎想要计算每个事件名称。一种方法是使用F#linq-sql只从数据库中引入事件的“名称”(没有其他列,因为你不需要它们),然后在F#中进行分组和映射操作。它可以帮助您提高性能,但不确定改进程度。