64 位 F#/FSI:非常大的单个文件功能的限制
64 bit F# / FSI: Limitation of very large single file function
我有一些化学模型,它们基于一些系数的稀疏矩阵。因此,在给定模型参数的情况下,我仅根据这些系数的非零元素生成 F# 代码。然后将生成的模型馈送到 ALGLIB (http://www.alglib.net/) ODE 求解器。系数矩阵大约 99.9% 到 99.99% 是稀疏的,这意味着只有 0.01% 到 0.1% 的系数不是精确的零。下面是一个非常 的简化示例,说明生成的 F# 模型文件的外观。它是使用 64 位 FSI 馈送到 ALGLIB ODE 求解器的函数 update (x : array<double>) : array<double>
。
现在,ALGLIB ODE 求解器完全能够为一个简单的输入函数处理至少 1M 的变量。我已经测试过了,它没有问题。对于典型模型,我有不到 10K 个变量。
然而,当我增加模型大小时,我在 运行 时间开始出现堆栈溢出异常:具有大约 100K LOC 的模型工作正常,但是具有大约 150K LOC 的模型因堆栈溢出异常而失败。
我猜测这与处理大型 "hard-coded" 数组的初始化/处理方式有关,我想知道我应该如何调整生成的代码 OR 如何才能我增加了 FSI and/or 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)
]
}
总结我们的发现。
经过一系列错误测试、跟踪和调试不同的假设后,@Konstantin 能够发现问题是由 JIT 编译器引起的。显然它试图在第一次执行之前编译 update
函数。此函数太大,导致堆栈溢出。
将函数拆分成更小的函数是解决方案。
好样的康斯坦丁!
我有一些化学模型,它们基于一些系数的稀疏矩阵。因此,在给定模型参数的情况下,我仅根据这些系数的非零元素生成 F# 代码。然后将生成的模型馈送到 ALGLIB (http://www.alglib.net/) ODE 求解器。系数矩阵大约 99.9% 到 99.99% 是稀疏的,这意味着只有 0.01% 到 0.1% 的系数不是精确的零。下面是一个非常 的简化示例,说明生成的 F# 模型文件的外观。它是使用 64 位 FSI 馈送到 ALGLIB ODE 求解器的函数 update (x : array<double>) : array<double>
。
现在,ALGLIB ODE 求解器完全能够为一个简单的输入函数处理至少 1M 的变量。我已经测试过了,它没有问题。对于典型模型,我有不到 10K 个变量。
然而,当我增加模型大小时,我在 运行 时间开始出现堆栈溢出异常:具有大约 100K LOC 的模型工作正常,但是具有大约 150K LOC 的模型因堆栈溢出异常而失败。
我猜测这与处理大型 "hard-coded" 数组的初始化/处理方式有关,我想知道我应该如何调整生成的代码 OR 如何才能我增加了 FSI and/or 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)
]
}
总结我们的发现。
经过一系列错误测试、跟踪和调试不同的假设后,@Konstantin 能够发现问题是由 JIT 编译器引起的。显然它试图在第一次执行之前编译 update
函数。此函数太大,导致堆栈溢出。
将函数拆分成更小的函数是解决方案。
好样的康斯坦丁!