64 бит F # / FSI: ограничение очень большой функции одного файла - PullRequest
0 голосов
/ 28 ноября 2018

У меня есть несколько химических моделей, которые основаны на разреженных матрицах некоторых коэффициентов.Итак, учитывая параметры модели, я генерирую код F # на основе только ненулевых элементов этих коэффициентов.Сгенерированная модель затем подается в ALGLIB (http://www.alglib.net/) ODE solver. Матрицы коэффициентов имеют разреженность от 99,9% до 99,99%, что означает, что только от 0,01% до 0,1% коэффициентов не являются точными нулями. Ниже приведен очень упрощенный пример того, как выглядит сгенерированный файл модели F #. Это функция update (x : array<double>) : array<double>, которая подается в решатель ALGLIB ODE с использованием 64-битного FSI.

Теперь, ALGLIB ODESolver вполне способен обрабатывать по крайней мере 1 МБ переменных для простой функции ввода. Я протестировал это, и это без проблем работает. У меня типичная переменная меньше 10 КБ.

Однако, когда я увеличиваюРазмер модели, я получаю исключение переполнения стека во время выполнения: модель с около 100K LOC работает нормально, но модель с около 150K LOC дает сбой с исключением переполнения стека.

Я предполагаю, что это связано с тем, какинициализация / обработка больших «жестко закодированных» массивов выполняется, и мне интересно, как я должен настроить сгенерированный код ИЛИ чТеперь я могу увеличить размер стека для FSI и / или 64-битной программы F #, скажем, до 1 ГБ.

Подчеркну, что это не типичная проблема переполнения стека рекурсивных функций, а только общий размермодели, которая вызывает проблему.

Если вы посмотрите на функцию 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)
                ]
        }

1 Ответ

0 голосов
/ 29 ноября 2018

Подводя итог нашим выводам.

После серии испытаний, тестирования ошибок, отслеживания и отладки различных гипотез @Konstantin удалось обнаружить, что проблема была в компиляторе JIT.Очевидно, он пытался скомпилировать функцию update до ее первого выполнения.Эта функция была слишком большой, и это вызывало переполнение стека.

Решением было разделение функции на более мелкие.

Браво Константин!

...