Возможно, для этого можно использовать механизм параллельных вычислений? Вот грубая установка, которая иллюстрирует идею:
Needs["SubKernels`LocalKernels`"]
doSomeWork[input_] := {$KernelID, Length[input], RandomReal[]}
getTheJobDone[] :=
Module[{subkernel, initsub, resultSoFar = {}}
, initsub[] :=
( subkernel = LaunchKernels[LocalMachine[1]]
; DistributeDefinitions["Global`"]
)
; initsub[]
; While[Length[resultSoFar] < 1000
, DistributeDefinitions[resultSoFar]
; Quiet[ParallelEvaluate[doSomeWork[resultSoFar], subkernel]] /.
{ $Failed :> (Print@"Ouch!"; initsub[])
, r_ :> AppendTo[resultSoFar, r]
}
]
; CloseKernels[subkernel]
; resultSoFar
]
Это слишком сложная настройка для создания списка из 1000 троек чисел. getTheJobDone
запускает цикл, который продолжается до тех пор, пока список результатов не будет содержать нужное количество элементов. Каждая итерация цикла оценивается в подядре. Если оценка подядра не удалась, то подядро перезапускается. В противном случае его возвращаемое значение добавляется в список результатов.
Чтобы попробовать это, оцените:
getTheJobDone[]
Чтобы продемонстрировать механизм восстановления, откройте окно Parallel Kernel Status и время от времени убивайте подядро. getTheJobDone
почувствует боль и напечатает Ой! всякий раз, когда подядро умирает. Однако общая работа продолжается и возвращается окончательный результат.
Обработка ошибок здесь очень грубая и, вероятно, должна быть поддержана в реальном приложении. Кроме того, я не исследовал, будет ли действительно серьезная ошибка в подядрах (например, нехватка памяти) оказывать неблагоприятное влияние на основное ядро. Если это так, то, возможно, подъядерные ядра могут убить себя, если MemoryInUse[]
превысит заданный порог.
Обновление - изоляция основного ядра от сбоев в ядре
Играя с этой платформой, я обнаружил, что любое использование общих переменных между основным ядром и подядром приводит к нестабильной работе Mathematica в случае сбоя подядра. Это включает использование DistributeDefinitions[resultSoFar]
, как показано выше, а также явные общие переменные с использованием SetSharedVariable
.
Чтобы обойти эту проблему, я передал resultSoFar
через файл. Это исключило синхронизацию между двумя ядрами, что привело к тому, что основное ядро оставалось в блаженном неведении о сбое в работе ядра. У этого также был хороший побочный эффект сохранения промежуточных результатов в случае основного сбоя ядра. Конечно, это также делает вызовы подядра немного медленнее. Но это не может быть проблемой, если каждый вызов подядра выполняет значительный объем работы.
Вот пересмотренные определения:
Needs["SubKernels`LocalKernels`"]
doSomeWork[] := {$KernelID, Length[Get[$resultFile]], RandomReal[]}
$resultFile = "/some/place/results.dat";
getTheJobDone[] :=
Module[{subkernel, initsub, resultSoFar = {}}
, initsub[] :=
( subkernel = LaunchKernels[LocalMachine[1]]
; DistributeDefinitions["Global`"]
)
; initsub[]
; While[Length[resultSoFar] < 1000
, Put[resultSoFar, $resultFile]
; Quiet[ParallelEvaluate[doSomeWork[], subkernel]] /.
{ $Failed :> (Print@"Ouch!"; CloseKernels[subkernel]; initsub[])
, r_ :> AppendTo[resultSoFar, r]
}
]
; CloseKernels[subkernel]
; resultSoFar
]