Запуск Geant4 в многоядерном режиме, теория

Запуск Geant4 в многоядерном режиме

Начиная с версии Geant4 10.0 появилась возможность запускать проекты на нескольких ядрах одновременно. При этом, как правило, больших проблем при миграции в мультипоточный режим не наблюдается, однако есть некоторые нюансы.

Для того, что бы проект можно было бы собрать в многопоточном режиме, при установке Geant4 в опциях CMAKE следует указать -DGEANT4_BUILD_MULTITHREADED=ON. Когда вы собираете проект с мультипоточной версией Geant4, то и проект сможет использовать мнопопоточный режим, в ином случае нет.

Вместо привычного класса G4RunManager для последовательного выполнения программы следует использовать многопоточную версию G4MTRunManager. Так же в коде можно использовать условие, с какой версией Джанта компилируется проект.

#ifdef G4MULTITHREADED
  G4MTRunManager* runManager = new G4MTRunManager;
  //Выбор максимального числа потоков в выполняемой системе (на компьютере)
  // либо можно вручную задать число потоков
  runManager->SetNumberOfThreads( G4Threading::G4GetNumberOfCores());
#else
  G4RunManager* runManager = new G4RunManager;
#endif

И в подключаемых заголовках указываем

// RunManager, класс из ядра Geant4,
//должен быть включен обязательно
#ifdef G4MULTITHREADED
#include "G4MTRunManager.hh"//Многопоточный
#else
#include "G4RunManager.hh"//Непараллельный, однопоточное моделирование
#endif

Еще необходимо подправить пользовательский класс наследуемый от

G4VUserActionInitialization

В нем появляется функция

virtual void BuildForMaster() const;

Также есть в многопоточном режиме один неожиданный сюрприз : не работает класс чуствительного объема

G4VSensitiveDetector

и наследуемые от него классы. Выходные файлы программы на Geant4 будут пустыми, так как просто ниразу не будет вызван метод

ProcessHits()

Для вывода данных (readout), необходимо переопределить класс

G4UserSteppingAction

у которого в функцию

virtual void UserSteppingAction(const G4Step*);

практически переносим код из

ProcessHits().

Для того, что бы данные выводились только с нужного объема, используем условие проверки текущего объема

if("nameOfMyDetecror"==track->GetVolume()->GetName()){
//Записываем события
}

Для сбора данных расчета с разных потоков (нитей) выполнения, используется специальный класс

 G4Accumulable

Он хорошо работает с простыми типами:double, int, float и другими. Однако можно к нему подключить и свой класс, в качестве примера потом будет написана статья.

Выводить данные конечно лучше только в главном процессе.

Моделирование в Geant4 на нескольких ядрах
Загрузка процессора при расчете на всех ядрах системы

Основные технические детали уже приведены, работающий пример с многопоточностью можно найти здесь, далее обсудим некоторые важные теоретические вопросы работы класса

G4MTRunManager

G4MTRunManager работает с общей памятью, т. е. часть классов, их данные общие для всех процессов, как например G4VUserDetectorConstruction, а часть классов являются локальными, т.е. уникальными для каждого потока.

Из этого следует ограничения на распаралеливание проектов, в частности, запускать распаралеленый таким образом проект на Geant4 лучше на одной многопроцессорной машине или на одной ноде суперкомпьютера, что бы избежать существенного падения производительности. Так же пробные запуски показали, что существенную роль играет объем кэша в процессора, чем он больше, тем больше быстродействие. Есть работа, в которой показано, что после 60 потоков начинается существенное падение производительности распаралеливания Geant4, хотя как я понимаю это все зависит от железа. Особенно актуально распаралеливание для тех, кто моделирует оптические фотоны, с помощью распаралеливания можно существенно сократить время счета.