@Robotex

Как реализовать поэлементное перемножение массивов средствами OpenMP, CUDA и GLSL?

Вот уже несколько дней бьюсь над решением этой проблемы, но мои решения получаются медленнее чем последовательное однопоточное решение:

for(int i=0;i<LAYERS;i++)
  {
    for(int j=0;j<INPUTS*NEURONS;j++)
    {
      temp[j] = inputs[j%INPUTS] * weights[i][j];
    }
    
    for(int j=0;j<NEURONS;j++)
    {
      inputs[j] = 0;
      
      for(int l=0;l<INPUTS;l++)
      {
        inputs[j] += temp[j*INPUTS + l];
      }
      
      inputs[j] = sigmoid(inputs[j]);
    }
  }



Т.е. если вкратце, то имеем массив в N элементов, массив в N*M, и массив (или матрица, здесь weight) в N*M*K элементов.

Поэлементно перемножаем первый массив на группу из K элементов третьего (когда в первом кончаются элементы, переходим на первый и вновь идем по его элементам) и результат во второй. Далее суммируем во втором массиве группы по M элементов и пишем суммы в первый массив (предварительно применив некоторую функцию, но не суть важно). Далее повторяем все с новой группой элементов третьего массива.


Так вот, как я уже сказал, мои решения намного медленнее, чем приведенный выше код (openMP медленнее на чуть-чуть :) ). Как же это сделать правиль?
  • Вопрос задан
  • 5355 просмотров
Пригласить эксперта
Ответы на вопрос 8
svetlov
@svetlov
Кажется, во втором варианте второй цикл не параллелится. Замените input[j] на локальную переменную, чтобы она в регистр упала. Еще может потребоваться промежуточный массив для складывания этих input[j] — чтобы потом одним махом переключить на него после выхода за цикл.
Ответ написан
Комментировать
@Robotex Автор вопроса
Вот для CUDA пробую использовать cublas, перемножение матриц нашел, а вот как перемножить два массива не пойму.
Ответ написан
Комментировать
@Robotex Автор вопроса
В чем принципиальная разница между этим: pastebin.com/8dpNKBwt (работает быстрее однопотокового аналога) и этим pastebin.com/fbe4gZSn (работает медленнее)?
Ответ написан
Комментировать
@Robotex Автор вопроса
Так, с OpenMP решил вот так: pastebin.com/sJ4fXiAb Теперь вроде заметно ускорение.
Сейчас думаю над CUDA. пока тоже все получается вроде.

А вот с GLSL беда. Я вообще не знаком с этой технологией, может кто помочь?
Ответ написан
svetlov
@svetlov
Ну смотрите.
Там нет массивов.
Создаете окошко — это обязательно.
Инииализируете конвейер.
Создаете vertex buffer object с координатами вершин. Да, я знаю что вершин у вас нет — но иначе шейдер не запустить, он будет работать по разу на вершину. Вершины могут быть фейковые.
Создаете текстуры. Современные видеокарты поддерживают текстуры из float — проблем быть не должно. Но следует убедиться для той видюхи, на которой все запускается — иначе нужно использовать другой способ расчета.
Устанавливаем все это добро как render state. Запускаем созданный и скомпилированный шейдер.
Он берет значения из текстур и рисует нам картинку. Вернее, шейдеров нужно два: вершинный и пиксельный. Второй должен записать результат. Есть еще выкрутасы с записью промежуточных результатов из шейдера — но тут нужно смотреть, что умеет конкретная видюха.
Берем результат. Откуда — вопрос. Если поддерживаются pixel buffer object и render target — то из PBO. Иначе из буфера экрана (надеюсь, до этого дело не дойдет, видюха будет более или менее сносной).
Может, я еще что-то забыл.

Перечисленная схема — лишь краткое изложение. На самом деле кода получается — мама не горюй. На «простое перемножение массивов» это ни разу не похоже.
Ответ написан
@Robotex Автор вопроса
CUDA: pastebin.com/TkrhuEWA
Что-то не так. Если L равно 1, то считает верно, если 2 и выше, то какие-то запредельные числа выходят.


for(int j=0;j<L;j++)
        {
          mulKernel<<<blocksMul, threadsMul>>>(devTemp, devInputs, devWeights, j*N*I, I);
          
          sumKernel<<<blocksSum, threadsSum>>>(devInputs, devTemp, N);
        }
Ответ написан
Комментировать
svetlov
@svetlov
Что-то мне кажется, что без __syncthreads() дела не будет.
Ответ написан
@Robotex Автор вопроса
Здесь: pastebin.com/ggETxnX8 реализовано умножение двух массивов.

У меня несколько вопросов:
1. Количество элементов тут должно быть кратно 4. Как исправить, чтобы работать с любым количеством.
2. Откуда кернел знает с каким индексом он работает?
3. Как использовать два кернела и запускать один после другого?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы