Самозаряжающийся MathKernel - возможно ли это в Mathematica? - PullRequest
9 голосов
/ 23 октября 2011

Этот вопрос возник из недавнего вопроса " Правильный способ ограничения Mathematica использование памяти? "

Интересно, возможно ли программно перезапустить MathKernel, сохраняятекущий процесс FrontEnd подключен к новому процессу MathKernel и оценивает некоторый код в новом сеансе MathKernel?Я имею в виду «прозрачный» перезапуск, который позволяет пользователю продолжать работать с FrontEnd, имея при этом новый свежий процесс MathKernel с некоторым кодом из предыдущего ядра, оцененным или оцененным в нем?

Мотивация вопроса состоит в том, чтобыспособ автоматизировать перезапуск MathKernel, когда он занимает слишком много памяти , не прерывая вычисления .Другими словами, вычисление должно автоматически продолжаться в новом процессе MathKernel без взаимодействия с пользователем (но с сохранением способности пользователя взаимодействовать с Mathematica , как это было изначально).Детали того, какой код должен оцениваться в новом ядре, конечно же, специфичны для каждой вычислительной задачи.Я ищу общее решение, как автоматически продолжить вычисления.

Ответы [ 6 ]

7 голосов
/ 06 декабря 2012

Из комментария от Arnoud Buzing вчера, в чате Stack Exchange Mathematica, цитата целиком:

В записной книжке, если у вас несколько ячеек, вы можете поместить Quit в ячейку самостоятельнои установите эту опцию:

SetOptions[$FrontEnd, "ClearEvaluationQueueOnKernelQuit" -> False]

Затем, если у вас есть ячейка над ней и под ней, выберите все три и выполните оценку, ядро ​​выйдет, но очередь оценки внешнего интерфейса продолжится (и перезапустит ядро ​​дляпоследняя ячейка).

- Arnoud Buzing

5 голосов
/ 23 октября 2011

При следующем подходе одно ядро ​​запускает внешний интерфейс с собственным ядром, которое затем закрывается и снова открывается, обновляя второе ядро.

Этот файл является входом MathKernel, C: \ Temp \ test4.m

Needs["JLink`"];
$FrontEndLaunchCommand="Mathematica.exe";
UseFrontEnd[
nb = NotebookOpen["C:\\Temp\\run.nb"];
SelectionMove[nb, Next, Cell];
SelectionEvaluate[nb];
];
Pause[8];
CloseFrontEnd[];
Pause[1];
UseFrontEnd[
nb = NotebookOpen["C:\\Temp\\run.nb"];
Do[SelectionMove[nb, Next, Cell],{12}];
SelectionEvaluate[nb];
];
Pause[8];
CloseFrontEnd[];
Print["Completed"]

Демонстрационная записная книжка C: \ Temp \ run.nb содержит две ячейки:

x1 = 0;
Module[{}, 
 While[x1 < 1000000, 
  If[Mod[x1, 100000] == 0, Print["x1=" <> ToString[x1]]]; x1++];
 NotebookSave[EvaluationNotebook[]];
 NotebookClose[EvaluationNotebook[]]]

Print[x1]
x1 = 0;
Module[{}, 
 While[x1 < 1000000, 
  If[Mod[x1, 100000] == 0, Print["x1=" <> ToString[x1]]]; x1++];
 NotebookSave[EvaluationNotebook[]];
 NotebookClose[EvaluationNotebook[]]]

Исходное ядро ​​открывает внешний интерфейс и запускает первую ячейку, затем выходит из внешнего интерфейса, открывает его снова и запускает вторую ячейку.

Все это может быть выполнено либо путем вставки (за один раз) ввода MathKernel в сеанс ядра, либо его можно запустить из пакетного файла, например, C: \ Temp \ RunTest2.bat

@echo off
setlocal
PATH = C:\Program Files\Wolfram Research\Mathematica\8.0\;%PATH%
echo Launching MathKernel %TIME%
start MathKernel -noprompt -initfile "C:\Temp\test4.m"
ping localhost -n 30 > nul
echo Terminating MathKernel %TIME%
taskkill /F /FI "IMAGENAME eq MathKernel.exe" > nul
endlocal

Это немного сложнее в настройке, и в его нынешнем виде это зависит от того, как долго будет ждать, прежде чем закрыть и перезапустить второе ядро.

4 голосов
/ 29 октября 2011

Возможно, для этого можно использовать механизм параллельных вычислений? Вот грубая установка, которая иллюстрирует идею:

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
  ]
3 голосов
/ 10 марта 2016

У меня аналогичное требование, когда я запускаю функцию CUDAFunction для длинного цикла, и у CUDALink не хватает памяти (аналогично: https://mathematica.stackexchange.com/questions/31412/cudalink-ran-out-of-available-memory). Нет улучшения утечки памяти даже с последней версией Mathematica 10.4. Я полагаю,обойти это здесь и надеяться, что вы найдете его полезным. Идея состоит в том, что вы используете сценарий bash для вызова программы Mathematica (запускаемой в пакетном режиме) несколько раз с передачей параметров из сценария bash. Вот подробные инструкции и демонстрационная программа(Это для ОС Windows):

  • Чтобы использовать bash-скрипт в Win_OS, вам нужно установить cygwin (https://cygwin.com/install.html).
  • ). Преобразуйте записную книжку Mathematica в пакет (.m) ввозможность использования в режиме сценария. Если вы сохраните свой блокнот с помощью команды «Сохранить как ...», все команды будут преобразованы в комментарии (это было отмечено Wolfram Research), поэтому лучше создать пакет (Файл-> Создать-Package), затем скопируйте и вставьте в него свои команды.
  • Напишите сценарий bash, используя редактор Vi (вместо Notepad или gedi).t для окна), чтобы избежать проблемы "\ r" (http://www.linuxquestions.org/questions/programming-9/shell-scripts-in-windows-cygwin-607659/).

Вот демонстрационная версия файла test.m

str=$CommandLine;
len=Length[str];
Do[
If[str[[i]]=="-start",
start=ToExpression[str[[i+1]]];
Pause[start];
Print["Done in ",start," second"];
];
,{i,2,len-1}];

Этот код математики читаетпараметр из командной строки и использовать его для расчета.Вот скрипт bash (script.sh) для многократного запуска test.m с разными параметрами.

#c:\cygwin64\bin\bash
for ((i=2;i<10;i+=2))
do
math -script test.m -start $i
done

В терминале cygwin введите «chmod a + x script.sh», чтобы включить сценарий, а затемможете запустить его, набрав "./script.sh".

2 голосов
/ 23 октября 2011

Вы можете программно завершить работу ядра, используя Exit[]. Внешний интерфейс (ноутбук) автоматически запустит новое ядро ​​при следующей попытке оценить выражение.

Сохранение «некоторого кода из предыдущего ядра» будет более сложным. Вы должны решить, что вы хотите сохранить. Если вы думаете, что хотите сохранить все, нет смысла перезагружать ядро. Если вы знаете, какие определения вы хотите сохранить, вы можете использовать DumpSave, чтобы записать их в файл перед завершением работы ядра, а затем использовать <<, чтобы загрузить этот файл в новое ядро.

С другой стороны, если вы знаете, какие определения занимают слишком много памяти, вы можете использовать Unset, Clear, ClearAll или Remove, чтобы удалить эти определения. Вы также можете установить для $ HistoryLength значение, меньшее Infinity (по умолчанию), если туда направляется ваша память.

0 голосов
/ 23 октября 2011

Звучит как работа для CleanSlate.

<< Utilities`CleanSlate`;
CleanSlate[]

От: http://library.wolfram.com/infocenter/TechNotes/4718/

"CleanSlate пытается сделать все возможное, чтобы вернуть ядро ​​в состояние, в котором оно было при первоначальной загрузке пакета CleanSlate.m."

...