首页 > 解决方案 > 64 位 F#/FSI:超大单文件功能的限制

问题描述

我有一些化学模型,它们基于一些系数的稀疏矩阵。因此,给定模型参数,我仅根据这些系数的非零元素生成 F# 代码。然后将生成的模型馈送到 ALGLIB ( http://www.alglib.net/ ) ODE 求解器。系数矩阵大约有 99.9% 到 99.99% 稀疏,这意味着只有 0.01% 到 0.1% 的系数不是精确的零。下面是一个非常简化的示例,展示了生成的 F# 模型文件的外观。它是update (x : array<double>) : array<double>使用 64 位 FSI 提供给 ALGLIB ODE 求解器的函数。

现在,ALGLIB ODE 求解器完全有能力为一个简单的输入函数处理至少 1M 个变量。我已经测试过了,它没有问题。对于一个典型的模型,我有不到 10K 的变量。

但是,当我增加模型大小时,我开始在运行时出现堆栈溢出异常:具有大约 100K LOC 的模型工作正常,但具有大约 150K LOC 的模型因堆栈溢出异常而失败。

我猜这与如何处理大型“硬编码”数组的初始化/处理有关,我想知道我应该如何调整生成的代码如何增加 FSI 和/或 F# 64 位程序的堆栈大小,假设为 1 GB。

我要强调的是,这不是一个典型的递归函数堆栈溢出问题,而只是导致问题的模型的整体大小。

如果你看一下update函数,你会看到它有一个生成的数组,其中的每个元素都是通过获取另一个数组并应用来生成的|> Array.sum。对于大型模型来说,这变得巨大,我猜这可能会导致堆栈溢出。

非常感谢!

PS 下面是一个非常简化的模型示例。它确实具有真实模型中出现的所有必要结构。

namespace Model

open Clm.Substances
open Clm.Model

open Clm.ReactionTypes

module ModelData = 
    let seedValue = 123456
    let numberOfAminoAcids = NumberOfAminoAcids.OneAminoAcid
    let maxPeptideLength = MaxPeptideLength.TwoMax
    let numberOfSubstances = 7

    let aminoAcids = AminoAcid.getAminoAcids numberOfAminoAcids
    let chiralAminoAcids = ChiralAminoAcid.getAminoAcids numberOfAminoAcids
    let peptides = Peptide.getPeptides maxPeptideLength numberOfAminoAcids

    let allSubst = 
        [ Substance.food ]
        @
        (chiralAminoAcids |> List.map (fun a -> Chiral a))
        @
        (peptides |> List.map (fun p -> PeptideChain p))

    let allInd = allSubst |> List.mapi (fun i s -> (s, i)) |> Map.ofList


    let getTotalSubst (x : array<double>) = 
        [|
            x.[0] // Y
            x.[1] // A
            x.[2] // a
            2.0 * x.[3] // AA
            2.0 * x.[4] // Aa
            2.0 * x.[5] // aA
            2.0 * x.[6] // aa
        |]
         |> Array.sum


    let getTotals (x : array<double>) = 
        [|
            // A
            (
                [|
                    x.[1] // A
                    2.0 * x.[3] // AA
                    x.[4] // Aa
                    x.[5] // aA
                |]
                |> Array.sum
                ,
                [|
                    x.[2] // a
                    x.[4] // Aa
                    x.[5] // aA
                    2.0 * x.[6] // aa
                |]
                |> Array.sum
            )
        |]

    let update (x : array<double>) : array<double> = 
        let xSum = (x |> Array.sum) - x.[0]

        let xSumN = 
            [|
                1.0 * x.[1] // A
                1.0 * x.[2] // a
                2.0 * x.[3] // AA
                2.0 * x.[4] // Aa
                2.0 * x.[5] // aA
                2.0 * x.[6] // aa
            |]
            |> Array.sum

        let xSumSquaredN = 
            [|
                1.0 * x.[1] * x.[1] // A
                1.0 * x.[2] * x.[2] // a
                2.0 * x.[3] * x.[3] // AA
                2.0 * x.[4] * x.[4] // Aa
                2.0 * x.[5] * x.[5] // aA
                2.0 * x.[6] * x.[6] // aa
            |]
            |> Array.sum

        [|

            // 0 - Y
            [|

                0.0001 * x.[2] // a | SynthesisName: Y <-> a
                -0.001 * x.[0] // Y | SynthesisName: Y <-> a
                0.0001 * x.[1] // A | SynthesisName: Y <-> A
                -0.001 * x.[0] // Y | SynthesisName: Y <-> A
            |]
            |> Array.sum

            // 1 - A
            [|

                0.0001 * x.[5] // aA | LigationName: a + A <-> aA
                -0.001 * x.[2] * x.[1] // a + A | LigationName: a + A <-> aA
                0.0001 * x.[4] // Aa | LigationName: A + a <-> Aa
                -0.001 * x.[1] * x.[2] // A + a | LigationName: A + a <-> Aa
                0.0001 * x.[3] // AA | LigationName: A + A <-> AA
                0.0001 * x.[3] // AA | LigationName: A + A <-> AA
                -0.001 * x.[1] * x.[1] // A + A | LigationName: A + A <-> AA
                -0.001 * x.[1] * x.[1] // A + A | LigationName: A + A <-> AA
                -0.0001 * x.[1] // A | SynthesisName: Y <-> A
                0.001 * x.[0] // Y | SynthesisName: Y <-> A
            |]
            |> Array.sum

            // 2 - a
            [|

                0.0001 * x.[5] // aA | LigationName: a + A <-> aA
                -0.001 * x.[2] * x.[1] // a + A | LigationName: a + A <-> aA
                0.0001 * x.[4] // Aa | LigationName: A + a <-> Aa
                -0.001 * x.[1] * x.[2] // A + a | LigationName: A + a <-> Aa
                0.0001 * x.[6] // aa | LigationName: a + a <-> aa
                0.0001 * x.[6] // aa | LigationName: a + a <-> aa
                -0.001 * x.[2] * x.[2] // a + a | LigationName: a + a <-> aa
                -0.001 * x.[2] * x.[2] // a + a | LigationName: a + a <-> aa
                -0.0001 * x.[2] // a | SynthesisName: Y <-> a
                0.001 * x.[0] // Y | SynthesisName: Y <-> a
            |]
            |> Array.sum

            // 3 - AA
            [|

                -0.0001 * x.[3] // AA | LigationName: A + A <-> AA
                0.001 * x.[1] * x.[1] // A + A | LigationName: A + A <-> AA
            |]
            |> Array.sum

            // 4 - Aa
            [|

                -0.0001 * x.[4] // Aa | LigationName: A + a <-> Aa
                0.001 * x.[1] * x.[2] // A + a | LigationName: A + a <-> Aa
            |]
            |> Array.sum

            // 5 - aA
            [|

                -0.0001 * x.[5] // aA | LigationName: a + A <-> aA
                0.001 * x.[2] * x.[1] // a + A | LigationName: a + A <-> aA
            |]
            |> Array.sum

            // 6 - aa
            [|

                -0.0001 * x.[6] // aa | LigationName: a + a <-> aa
                0.001 * x.[2] * x.[2] // a + a | LigationName: a + a <-> aa
            |]
            |> Array.sum

        |]

    let modelDataParams = 
        {
            numberOfSubstances = 7
            numberOfAminoAcids = OneAminoAcid
            maxPeptideLength = TwoMax
            getTotals = getTotals
            getTotalSubst = getTotalSubst
            allSubst = allSubst
            allInd = allInd

            allRawReactions = 
                [
                ]

            allReactions = 
                [
                    (SynthesisName, 2)
                    (LigationName, 4)
                ]
        }

标签: f#stack-overflowfsi

解决方案


总结我们的发现。

经过一系列试验,错误测试、跟踪和调试不同的假设@Konstantin 能够发现问题是由于 JIT 编译器造成的。显然,它试图在update第一次执行之前编译该函数。此函数太大,导致堆栈溢出。

将功能拆分为更小的功能是解决方案。

好棒的康斯坦丁!


推荐阅读