Я пытаюсь использовать OpenMP для ускорения кода для вычислений нейронной сети. Поскольку я использую Visual Studio 2017, мне нужно включить поддержку OpenMP на листах свойств. Однако после того, как я это сделал, некоторая часть кода замедляется примерно в 5 раз, хотя я не включаю в код любой #pragma omp.
Я изолировал разделы и обнаружил, что именно эта функция вызывает проблему:
void foo(Eigen::Matrix<float,3,Eigen::Dynamic> inputPts)
{
std::vector<Eigen::MatrixXf> activation;
activation.reserve(layerNo);
activation.push_back(inputPts);
int inputNo = inputPts.cols();
for (int i = 0; i < layerNo - 2; i++)
activation.push_back(((weights[i]*activation[i]).colwise()+bias[i]).array().tanh());
activation.push_back(((weights[layerNo - 2]*activation[layerNo - 2]).colwise()+bias[layerNo - 2]));
val = activation[layerNo - 1]/scalingFactor;
std::vector<Eigen::MatrixXf> delta;
delta.reserve(layerNo);
Eigen::Matrix<float, 1, Eigen::Dynamic> seed;
seed.setOnes(1, inputNo);
delta.push_back(seed);
for (int i = layerNo - 2; i >= 1; i--)
{
Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic>
d_temp = weights[i].transpose()*delta[layerNo - 2 - i],
d_temp2 = 1 - activation[i].array().square(),
deltaLayer = d_temp.cwiseProduct(d_temp2);
delta.push_back(deltaLayer);
}
grad = weights[0].transpose()*delta[layerNo - 2];
}
Два цикла for - это тот, который значительно замедляется (с ~ 3 мс до ~ 20 мс). Как ни странно, хотя эта функция вызывается в программе много раз, затрагиваются только некоторые из них.
Я включил заголовочный файл <omp.h>. Я не уверен, связано ли это с библиотекой Eigen, которая используется повсеместно. Я попытался определить EIGEN_DONT_PARALLELIZE и вызвать Eigen::initParallel(), как это предлагается в официальный сайт, но это не помогает.
Странно то, что я даже не включил parallel pragma вообще, не должно быть никаких накладных расходов на обработку функций OpenMP? Почему он все еще замедляется?





Матрично-матричные продукты Эйгена по умолчанию являются многопоточными, если включен OpenMP. Проблема скорее всего в сочетании:
Таким образом, решение состоит в том, чтобы ограничить количество потоков OpenMP количеством физических ядер, например, путем установки переменной среды OMP_NUM_THREADS. Вы также можете отключить многопоточность Eigen, определив макрос EIGEN_DONT_PARALLELIZE во время компиляции.
Больше информации в док.
Подробнее о том, как гиперпоточность может снизить производительность: С гиперпоточностью у вас есть два потока, работающих с чередованием на одном ядре. Они чередуют каждую инструкцию. Если ваши потоки не используют менее половины ресурсов ЦП (с точки зрения вычислений), то это победа, потому что вы будете использовать больше вычислительных единиц. Но если один поток уже использует 100% вычислительных единиц (как в случае хорошо оптимизированного матрично-матричного продукта), вы теряете производительность из-за 1) естественных накладных расходов на управление двумя потоками и 2) из-за того, что L1 кеш теперь используется двумя разными задачами. Матрично-матричные ядра разработаны с учетом точной емкости L1. С двумя потоками ваш кеш L1 становится почти неэффективным. Это означает, что вместо получения очень быстрого кеша L1 большую часть времени вы в конечном итоге получаете доступ к гораздо более медленному кешу L2, и, таким образом, вы получаете огромное падение производительности. В отличие от Linux и Windows, в OSX я не наблюдаю такого падения производительности, скорее всего, потому, что система может отменить расписание вторых потоков, если процессор уже слишком занят.
Это то, что я пытался объяснить в своем ответе, я расширил ответ более подробной информацией.
Спасибо за ответ. Я добавил строки
omp_set_num_threads(2); Eigen::setNbThreads(1); Eigen::initParallel();(см. ссылка на сайт), и время работы вернулось к норме. К сожалению, мои временные рамки не улучшаются, хотя я добавил операторы#pragma. Думаю, в моем случае параллельных потоков недостаточно. Просто нужно кое-что прояснить: как гиперпоточность вызывает эту проблему? Установка OMP_NUM_THREADS на 2 дает мне обычное время выполнения, но 4 замедляет код.