F # - fsc.exe зависает на огромном файле - PullRequest
0 голосов
/ 03 февраля 2019

Я управляю некоторыми моделями органической химии.Модель описывается сгенерированным файлом ModelData.fs, например: https://github.com/kkkmail/ClmFSharp/blob/master/Clm/Model/ModelData.fs.Файл имеет очень простую структуру, и использование сгенерированного файла модели - единственный способ, которым он может работать.

Ссылочный файл предназначен только для тестов, но реальные модели огромны и могут приближаться к 60 -70 МБ / 1,5 МЛ.Когда я пытаюсь скомпилировать такие файлы, F # компилятор fsc.exe просто зависает и никогда не возвращается.Он «съедает» около 1,5 ГБ памяти, а затем делает что-то навсегда при почти 100% -ой вычислительной мощности.Он может работать с небольшими моделями, которые занимают около 10 МБ менее чем за минуту.Так что где-то между 10 МБ и 70 МБ что-то плохо выходит из строя в fsc.

Интересно, есть ли какие-то настройки параметров, которые я мог бы сделать, чтобы fsc компилировал проект, чтобы сделать егоспособен обрабатывать такие огромные модели.

У огромных моделей, о которых я говорю, один параметр устанавливается следующим образом: let numberOfSubstances = 65643.Это приводит к различным сгенерированным массивам такого размера.Интересно, может ли это быть источником проблемы?

Большое спасибо!

1 Ответ

0 голосов
/ 04 февраля 2019

Я не думаю, что вам нужно автоматически генерировать все это.

Из ваших комментариев я понимаю, что функции d0, d1, ... генерируются из большой разреженной матрицы вспособ суммирования всего входного массива 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.Но это все несущественно с моей точки зрения.

Теперь в комментариях было предложено, что вы не генерируете функции d0, d1, ..., а вместо этого работаете напрямую с матрицей,Например, это было бы наивной реализацией такого предложения:

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, которую я дал выше, будет представлена ​​22-м элементом indicies:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...