Включение поддержки Open MP в Visual Studio 2017 замедляет выполнение кода

Я пытаюсь использовать 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? Почему он все еще замедляется?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
470
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Матрично-матричные продукты Эйгена по умолчанию являются многопоточными, если включен OpenMP. Проблема скорее всего в сочетании:

  1. Ваш процессор является гиперпоточным, например, у вас есть 4 физических ядра, способных запускать 8 потоков.
  2. OpenMP не позволяет узнать количество физических ядер, поэтому Eigen запустит 8 потоков.
  3. Ядро матрично-матричного произведения Эйгена полностью оптимизировано и использует почти 100% мощности ЦП. Следовательно, нет места для запуска двух таких потоков на одном ядре, и производительность значительно падает (загрязнение кеша).

Таким образом, решение состоит в том, чтобы ограничить количество потоков 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 замедляет код.

mjfoo21 15.11.2018 13:10

Это то, что я пытался объяснить в своем ответе, я расширил ответ более подробной информацией.

ggael 15.11.2018 13:53

Другие вопросы по теме