Возможно, для этого можно использовать параллельные вычислительные машины? Вот грубая установка, иллюстрирующая идею:
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
]
person
WReach
schedule
29.10.2011
MathLink
- но мой способ действительно очень сложный, крайне неэлегантный, полагается на большое количество недокументированных функций, которые потенциально зависят от версии. И код действительно огромен! Я надеюсь, что новая функциональностьScheduledTasks
может дать гораздо более элегантный способ решить эту проблему. - person Alexey Popkov   schedule 23.10.2011