Не видит каких-либо существенных улучшений при использовании параллельного блока в OpenMP C ++ - PullRequest
0 голосов
/ 13 мая 2018

Я получаю массив Eigen::MatrixXf и Eigen::Matrix4f в режиме реального времени.Оба этих массива имеют одинаковое количество элементов.Все, что я пытаюсь сделать, это просто умножить элементы обоих массивов вместе и сохранить результат в другом массиве с тем же индексом.

Пожалуйста, посмотрите фрагмент кода ниже -

#define COUNT 4

while (all_ok())
{
    Eigen::Matrix4f    trans[COUNT];
    Eigen::MatrixXf  in_data[COUNT];
    Eigen::MatrixXf out_data[COUNT];

    // at each iteration, new data is filled
    // in 'trans' and 'in_data' variables

    #pragma omp parallel num_threads(COUNT)
    {
        #pragma omp for
        for (int i = 0; i < COUNT; i++)
            out_data[i] = trans[i] * in_clouds[i];
    }
}

Пожалуйстаобратите внимание, что COUNT является константой.Размеры trans и in_data равны (4 x 4) и (4 x n) соответственно, где n составляет приблизительно 500 000.Чтобы распараллелить цикл for, я попробовал OpenMP, как показано выше.Однако я не вижу каких-либо существенных улучшений в истекшем времени цикла for.

Есть предложения?Любые альтернативы для выполнения той же операции, пожалуйста?

Редактировать: Моя идея - определить 4 (=COUNT) потока, каждый из которых заботится о умножении.Таким образом, нам не нужно каждый раз создавать потоки, я думаю!

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Работает для меня, используя следующий автономный пример, то есть я получаю ускорение в 4 раза при включении openmp:

#include <iostream>
#include <bench/BenchTimer.h>
using namespace Eigen;

const int COUNT = 4;

EIGEN_DONT_INLINE
void foo(const Matrix4f *trans, const MatrixXf *in_data, MatrixXf *out_data)
{
  #pragma omp parallel for num_threads(COUNT)
  for (int i = 0; i < COUNT; i++)
    out_data[i] = trans[i] * in_data[i];
}

int main()
{
  Eigen::Matrix4f    trans[COUNT];
  Eigen::MatrixXf  in_data[COUNT];
  Eigen::MatrixXf out_data[COUNT];
  int n = 500000;
  for (int i = 0; i < COUNT; i++)
  {
    trans[i].setRandom();
    in_data[i].setRandom(4,n);
    out_data[i].setRandom(4,n);
  }

  int tries = 3;
  int rep = 1;

  BenchTimer t;

  BENCH(t, tries, rep, foo(trans, in_data, out_data));

  std::cout << " " << t.best(Eigen::REAL_TIMER) << " (" << double(n)*4.*4.*4.*2.e-9/t.best() << " GFlops)\n";

  return 0;
}

Итак, 1) убедитесь, что вы измеряете время настенных часов, а не процессорное время, и 2) убедитесь, что продукты являются узким местом и не заполняют in_data.

Наконец, для максимальной производительности не забудьте включить AVX / FMA (например, с -march=native), и, конечно же, обязательно сравните тест с включенной оптимизацией компилятора.

Для справки: на моем компьютере приведенный выше пример занимает 0,25 с без openmp и 0,065 с с.

0 голосов
/ 14 мая 2018

Вам необходимо указать -fopenmp во время компиляции и компоновки. Но вы быстро достигнете предела, когда доступ к ОЗУ будет остановлен и ускорится. Вы действительно должны взглянуть на внутренние свойства вектора. В зависимости от вашего процессора вы можете ускорить ваши операции до размера вашего регистра, деленного на размер вашей переменной (float = 4). Так что, если ваш процессор поддерживает, скажем, AVX, вы будете иметь дело с 8 поплавками за раз. Если вам нужно вдохновение, вы можете украсть код из моей библиотеки реконструкции медицинских изображений здесь: https://github.com/kvahed/codeare/blob/master/src/matrix/SIMDTraits.hpp Код делает весь шебанг для числа с плавающей запятой / двойного вещественного и сложного.

...