Eigen и многопоточность
Запуск Eigen в параллельном режиме
Некоторые алгоритмы Eigen могут использовать несколько ядер процессора в вашей системе. Для этого достаточно включить OpenMP в вашем компиляторе, например:
- GCC:
-fopenmp - ICC:
-openmp - MSVC: проверьте соответствующий параметр в свойствах сборки.
Вы можете контролировать количество потоков, которые будут использоваться, используя либо API OpenMP, либо API Eigen с приоритетом:
OMP_NUM_THREADS=n ./my_program omp_set_num_threads(n); Eigen::setNbThreads(n);
Если setNbThreads не был вызван, Eigen использует количество потоков, указанное в OpenMP. Вы можете восстановить это поведение, вызвав setNbThreads(0);. Вы можете запросить количество используемых потоков с помощью:
n = Eigen::nbThreads( );
Вы можете отключить многопоточность Eigen во время компиляции, определив препроцессорный символ EIGEN_DONT_PARALLELIZE.
В настоящее время следующие алгоритмы могут использовать многопоточность:
- общие произведения плотных матриц
- PartialPivLU
- умножение разреженных матриц в порядке строк на вектор/матрицу
-
ConjugateGradient с
Lower|Upperв качествеUpLoшаблона. - BiCGSTAB с разреженной матрицей в формате строк.
- LeastSquaresConjugateGradient
- Предупреждение
- В большинстве ОС крайне важно ограничить количество потоков количеством физических ядер, иначе ожидается существенное снижение производительности, особенно для операций, включающих плотные матрицы.
Действительно, принцип гиперпоточности заключается в запуске нескольких потоков (в большинстве случаев 2) на одном ядре в перекрывающемся режиме. Однако ядро умножения матриц Eigen полностью оптимизировано и уже использует почти 100% мощности процессора. Следовательно, нет места для запуска нескольких таких потоков на одном ядре, и производительность значительно снижается из-за загрязнения кэша и других накладных расходов. На данном этапе чтения вы, вероятно, задаетесь вопросом, почему Eigen не ограничивает себя количеством физических ядер? Это просто потому, что OpenMP не позволяет узнать количество физических ядер, и поэтому Eigen запустит столько потоков, сколько ядер сообщает OpenMP.
Использование Eigen в многопоточном приложении
В случае, если ваше собственное приложение многопоточное, и несколько потоков обращаются к Eigen, необходимо инициализировать Eigen, вызвав следующую функцию до создания потоков:
#include <Eigen/Core> int main(int argc, char** argv) { Eigen::initParallel(); ... }
- Примечание
- В Eigen 3.3 и компиляторе, полностью совместимом с C++11 (т.е., безопасная для потоков инициализация статических локальных переменных), вызов
initParallel()является необязательным.
- Предупреждение
- Обратите внимание, что все функции, генерирующие случайные матрицы, не являются реентерабельными или потокобезопасными. К ним относятся DenseBase::Random() и DenseBase::setRandom(), даже после вызова
Eigen::initParallel(). Это связано с тем, что эти функции основаны наstd::rand, которая не является реентерабельной. Для потокобезопасного генератора случайных чисел рекомендуется использовать генераторы случайных чисел C++11 (пример) илиboost::random.
В случае, если ваше приложение распараллелено с помощью OpenMP, вы можете отключить собственную многопоточность Eigen, как описано в предыдущем разделе.
- Предупреждение
- Использование OpenMP с пользовательскими типами скаляров, которые могут генерировать исключения, может привести к непредсказуемому поведению при их генерации.
© Eigen.
Licensed under the MPL2 License.
https://eigen.tuxfamily.org/dox/TopicMultiThreading.html