我一直在用 F# 做一些计算密集型的工作。使用 .Net Task Parallel Library 之类的函数Array.Parallel.map
以指数级的速度加快了我的代码速度,而付出的努力非常少。
但是,由于内存问题,我重新编写了一段代码,以便可以在序列表达式中懒惰地评估它(这意味着我必须存储和传递更少的信息)。当需要评估时,我使用了:
// processor and memory intensive task, results are not stored
let calculations : seq<Calculation> = seq { ...yield one thing at a time... }
// extract results from calculations for summary data
PSeq.iter someFuncToExtractResults results
代替:
// processor and memory intensive task, storing these results is an unnecessary task
let calculations : Calculation[] = ...do all the things...
// extract results from calculations for summary data
Array.Parallel.map someFuncToExtractResults calculations
当使用任何 Array.Parallel 函数时,我可以清楚地看到我计算机上的所有内核都启动了(~100% CPU 使用率)。然而,所需的额外内存意味着程序永远不会完成。
当我运行程序时使用 PSeq.iter 版本,CPU 使用率只有 8% 左右(并且 RAM 使用率最低)。
那么:PSeq 版本运行这么慢有什么原因吗?是因为懒惰的评价吗?我缺少一些神奇的“平行”的东西吗?
谢谢,
其他资源,两者的源代码实现(它们似乎在 .NET 中使用不同的并行库):
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs
https://github.com/fsharp/powerpack/blob/master/src/FSharp.PowerPack.Parallel.Seq/pseq.fs
编辑:为代码示例和详细信息添加了更多详细信息
代码:
序列
// processor and memory intensive task, results are not stored let calculations : seq<Calculation> = seq { for index in 0..data.length-1 do yield calculationFunc data.[index] } // extract results from calculations for summary data (different module) PSeq.iter someFuncToExtractResults results
大批
// processor and memory intensive task, storing these results is an unnecessary task let calculations : Calculation[] = Array.Parallel.map calculationFunc data // extract results from calculations for summary data (different module) Array.Parallel.map someFuncToExtractResults calculations
细节:
- 存储中间数组版本在 10 分钟内快速运行(就崩溃前而言),但在崩溃之前使用 ~70GB RAM(64GB 物理,其余分页)
- seq 版本需要 34 分钟并使用一小部分 RAM(仅约 30GB)
- 我正在计算一个〜十亿的价值。因此,十亿双倍(每个 64 位)= 7.4505806GB。有更复杂的数据形式......我正在清理一些不必要的副本,因此当前大量的 RAM 使用。
- 是的,架构不是很好,懒惰的评估是我尝试优化程序和/或将数据分成更小的块的第一部分
- 使用较小的数据集,两个代码块输出相同的结果。
- @pad,我尝试了你的建议,当输入 Calculation[] 时,PSeq.iter 似乎工作正常(所有内核都处于活动状态),但仍然存在 RAM 问题(它最终崩溃了)
- 代码的摘要部分和计算部分都是 CPU 密集型的(主要是因为大数据集)
- 使用 Seq 版本,我的目标只是并行化一次