Многопоточность и мультипроцессорность

ROOT может использоваться как библиотека из нескольких потоков при условии использования специальной функции ROOT::EnableImplicitMT(numthreads). Эта функция обеспечивает глобальное включение неявной многопоточности в ROOT, активацию параллельного выполнения методов в ROOT, что обеспечивают внутреннюю параллелизацию.
Функция ROOT::EnableThreadSafety() oбеспечивает глобальный мьютекс, чтобы сделать ROOT потокобезопасным.
Мьютекс (англ. mutex, от mutual exclusion «взаимное исключение») аналог одноместного семафора, служащий в программировании для синхронизации одновременно выполняющихся потоков. Мьютекс отличается от семафора тем, что только владеющий им поток может его освободить, т.е. перевести в отмеченное состояние. Мьютексы это один из вариантов семафорных механизмов для организации взаимного исключения. Они реализованы во многих ОС, их основное назначение организация взаимного исключения для потоков из одного и того же или из разных процессов. Мьютексы это простейшие двоичные семафоры, которые могут находиться в одном из двух состояний отмеченном или неотмеченном (открыт и закрыт соответственно). Когда какой-либо поток, принадлежащий любому процессу, становится владельцем объекта mutex, последний переводится в неотмеченное состояние. Если задача освобождает мьютекс, его состояние становится отмеченным. Задача мьютекса защита объекта от доступа к нему других потоков, отличных от того, который завладел мьютексом. В каждый конкретный момент только один поток может владеть объектом, защищённым мьютексом. Если другому потоку будет нужен доступ к переменной, защищённой мьютексом, то этот поток блокируется до тех пор, пока мьютекс не будет освобождён. Цель использования мьютексов защита данных от повреждения в результате асинхронных изменений.
Методы ROOT::DisableImplicitMT() и ROOT::IsImplicitMTEnabled() используются для отключения и проверки состояния глобальной неявной многопоточности в ROOT соответственно. Также необходимо, чтобы в каждом потоке читался или записывался только один файл.
Мультипроцессорность
Инструмент, предоставляемый ROOT для выполнения простых операций с использованием многопроцессорности — это класс TProcPool. Новый интерфейс, реализованный в классе TProcPool, обеспечивает возможность параллельно выполнять очень общий набор задач, описываемых макросами или функциями. Основные методы класса TProcPool::Map(F func, unsigned nTimes) и TProcPool::MapReduce(F func, unsigned nTimes, R redfunk) анализируют деревья, используя все имеющиеся в наличии ядра.
По этой ссылке находятся обучающие программы, предназначенные для иллюстрации многоядерных особенностей ROOT, таких как осведомленность и безопасность потоков, многопоточность и многопроцессорность.
Программа, представленная ниже, демонстрирует, как активировать и использовать неявное распараллеливание метода TTree::GetEntry().
int imt001_parBranchProcessing()
{
//Сначала включаем неявную многопоточность, чтобы использовалась неявная параллелизация.
// Параметр вызова определяет количество потоков
int nthreads = 4;
ROOT::EnableImplicitMT(nthreads);
// Открываем файл, содержащий дерево
TFile *file = TFile::Open(«http://root.cern.ch/files/h1/dstarmb.root»);
// Вытаскиваем дерево
TTree *tree = nullptr;
file->GetObject(«h42», tree);
// Считаваем ветви параллельно.
// Интерфейс ничем не отличается от непараллельного случая, поскольку параллелизация внутренняя
for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
tree->GetEntry(i); // параллельное чтение
}
// IMT параллелизация может быть отключена для отдельного дерева
tree->SetImplicitMT(false);
// Если сейчас вызвать GetEntry, то считывание будет происходить последовательно
for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
tree->GetEntry(i); // последовательное считывание
}
// Параллельное считывание может быть восстановлено
tree->SetImplicitMT(true);
// IMT можно также отключить глобально.
ROOT::DisableImplicitMT();
// В этом случае даже если конкретному дереву разрешено параллельное считывание,
// считывание будет происходить последовательно
for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
tree->GetEntry(i); // последовательное считывание
}
return 0;
}
Такая параллелизация создает одну задачу для каждой ветви верхнего уровня анализируемого дерева. В этом примере большинство ветвей являются числами с плавающей запятой, которые очень быстро читаются. Однако эта параллелизация может использоваться и на больших деревьях со многими (сложными) ветвями. В этом случае выгода от ускорения будет более очевидной.