F#-fsc.exe挂在大文件上

时间:2019-02-03 19:01:14

标签: f#

我运行了一些有机化学模型。通过生成的ModelData.fs文件来描述模型,例如:https://github.com/kkkmail/ClmFSharp/blob/master/Clm/Model/ModelData.fs。该文件具有非常简单的结构,并且使用生成的模型文件是可能的唯一途径。

参考文件仅用于测试,但实际模型非常庞大,可能接近60-70 MB / 1.5M LOC。当我尝试编译此类文件时,F#编译器fsc.exe只会挂断,永远不会回来。它“吞噬”了约1.5 GB的内存,然后以接近100%的处理能力永远做某事。它可以清楚地处理较小的模型,这些模型在不到一分钟的时间内会占用大约10 MB的空间。因此,fsc中10到70 MB之间的某个地方发生了严重损坏。

我想知道是否可以对fsc编译项目的方式进行一些参数调整,以使其能够处理如此庞大的模型。

我要引用的大型模型的一个参数设置如下:let numberOfSubstances = 65643。这将导致生成各种大小的数组。我想知道这是否可能是问题的根源。

非常感谢!

1 个答案:

答案 0 :(得分:3)

我认为您不需要自动生成所有这些内容。

从您的评论中,我了解到d0d1,...函数是从大型稀疏矩阵生成的,其总和为所有输入数组x (具有系数),但关键是跳过对零系数求和,这会给您带来很大的性能提升,因为矩阵很大。那是正确的评估吗?

如果是这样,我仍然认为您不需要生成代码即可做到这一点。

让我们看看。我将假设您的巨型稀疏矩阵具有用于获取单元格值的接口,并且看起来像这样:

let getMatrixCell (i: int) (j: int) : double
let maxI: int
let maxJ: int

然后您的自动生成代码可能看起来像这样:

let generateDFunction (i: int) =
    printfn "let d%d (x: double[]) =" i
    printfn "    [|"
    for j in 0..maxJ do
        let cell = getMatrixCell i j
        if cell <> 0 then
            printfn "        %f * x.[%d]" cell j
    printfn "    |]"
    printfn "    |> Array.sum"

这将导致如下结果:

let d25 (x : array<double>) = 
    [|
        -1.0 * x.[25]
        1.0 * x.[3]
    |]
    |> Array.sum

请注意,我在这里进行了简化:在您的示例文件中,函数看起来也将负系数乘以x.[i]。但是也许我也太复杂了,因为看起来所有系数始终都是1-1。但这与我的观点无关。

现在,在注释中,建议您不要生成函数d0d1,...,而是直接使用矩阵。例如,这将是这种建议的幼稚实现:

let calculateDFunction (i: int) (x: double[]) =
    [| for j in 0..maxJ -> (getMatrixCell i j) * x.[j] |] |> Array.sum

然后,您认为该解决方案的速度将令人望而却步,因为它总是在整个数组x上进行迭代,该数组很大,但是大多数系数为零,因此不必这样做。

然后,解决此问题的方法是使用生成代码的中间步骤:您生成仅涉及非零索引的函数,然后编译并使用这些函数。

但这是重点:是的,您确实需要中间步骤来摆脱非零指数,但不必生成并编译代码

>

相反,您可以提前准备非零索引的列表/数组:

let indicies = 
    [| for i in 0..maxI ->
        [ for j in 0..maxJ do
            let cell = getMatrixCell i j
            if cell <> 0 then yield (j, cell)
        ]
    |]

这将产生一个数组indicies : Array<int list>,其中每个索引k对应于您的自动生成的函数dk,并且它包含一个非零矩阵索引列表以及它们在值中的值。矩阵。例如,我上面给出的功能d22将由indicies的第22个元素表示:

indicies.[22] = [ (25, -1.0), (3, 1.0) ]

基于此中间结构,然后可以计算任何函数dk

let calculateDFunction (k: int) (x: double[]) =
    [| for (j, coeff) in indicies.[k] -> coeff * x.[j] |] |> Array.sum

实际上,如果性能对您至关重要(似乎来自注释),则您可能应该取消所有这些中间数组:每次迭代中成百上千个堆分配绝对无济于事。您可以使用可变变量求和:

let calculateDFunction (k: int) (x: double[]) =
    let sum = 0.0
    for (j, coeff) in indicies.[k] do
        sum <- sum + coeff * x.[j]
    sum