Суммируйте переменную по всем потокам в ядре CUDA и верните ее хосту - PullRequest
0 голосов
/ 29 мая 2018

Я новичок в cuda, и я пытаюсь реализовать Ядро для расчета энергии моего Метрополиса Монте-Карло.

Я положу здесь линейную версию этой функции:

float calc_energy(struct frame frm, float L, float rc){
int i,j;
float E=0, rij, dx, dy, dz;
for(i=0; i<frm.natm; i++)
{
    for(j=i+1; j<frm.natm; j++)
    {
        dx = fabs(frm.conf[j][0] - frm.conf[i][0]);
        dy = fabs(frm.conf[j][1] - frm.conf[i][1]);
        dz = fabs(frm.conf[j][2] - frm.conf[i][2]);
        dx = dx - round(dx/L)*L;
        dy = dy - round(dy/L)*L;
        dz = dz - round(dz/L)*L;

        /*rij*/
        rij = sqrt(dx*dx + dy*dy + dz*dz);

        if (rij <= rc)
        {
            E = E + (4*((1/pow(rij,12))-(1/pow(rij,6))));
        }
    } 
}

return E;

Затем я попытаюсь распараллелить это с помощью Cuda: Это моя идея:

void calc_energy(frame* s, float L, float rc)
{

extern __shared__ float E;

int i = blockDim.x*blockIdx.x + threadIdx.x; 
int j = blockDim.y*blockIdx.y + threadIdx.y; 

float rij, dx, dy, dz;

dx = fabs(s->conf[j][0] - s->conf[i][0]);
dy = fabs(s->conf[j][1] - s->conf[i][1]);
dz = fabs(s->conf[j][2] - s->conf[i][2]);
dx = dx - round(dx/L)*L;
dy = dy - round(dy/L)*L;
dz = dz - round(dz/L)*L; 

rij = sqrt(dx*dx + dy*dy + dz*dz);

if (rij <= rc)
{
   E += (4*((1/pow(rij,12))-(1/pow(rij,6)))); //<- here is the big problem
}
} 

Мой главный вопрос - как суммировать переменную E из каждого потока и возвращать ее хосту?Я намереваюсь использовать как можно больше потоков и блоков.

Очевидно, что при вычислении переменной E часть кода отсутствует.

Я прочитал несколько вещей о методах сокращения, ноЯ хотел бы знать, если это необходимо здесь.

Я вызываю ядро, используя следующий код:

 calc_energy<<<dimGrid,dimBlock>>>(d_state, 100, 5);

edit :

Я понял, что мне нужно использовать методы сокращения. CUB отлично работает для меня.

Продолжая реализацию кода, я понял, что у меня возникла новая проблема, возможно, из-за недостатка знаний в этой области.

В моем вложенном цикле переменная (frm.natm) может достигать значений порядка 10 ^ 5.думая о моем графическом процессоре (GTX 750ti), число потоков на блок равно 1024, а количество блоков на сетку - 1024. Если я правильно понял, максимальное количество запусков в ядре равно 1024x1024 = 1048576 (меньше, чем на самом деле).

Так что, если мне нужно выполнить 10 ^ 5 x 10 ^ 5 = 10 ^ 10 вычислений в моем вложенном цикле, что будет лучшим способом придумать алгоритм?Выберите фиксированное число (которое подходит для моего графического процессора) и разделите вычисления, было бы хорошей идеей?

Ответы [ 2 ]

0 голосов
/ 22 января 2019
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/generate.h>
#include <thrust/reduce.h>
#include <thrust/functional.h>
#include <algorithm>
#include <cstdlib>

int main(void)
{
    thrust::host_vector<int> h_vec(100);
    std::generate(h_vec.begin(), h_vec.end(), rand);

    thrust::device_vector<int> d_vec = h_vec;
    int x = thrust::reduce(d_vec.begin(), d_vec.end(), 0, thrust::plus<int>());
    std::cout<< x<< std::endl;
    return 0;
}
0 голосов
/ 29 мая 2018

Мой главный вопрос заключается в том, как суммировать переменную E из каждого потока и возвращать ее на хост?

Вам нужно будет подсчитать каждый поток вычислений в блоке *Уровень 1006 * сначала с использованием какой-либо формы параллельного сокращения блоков (для этого я рекомендую реализацию поэтапного сокращения блоков CUB).

Как только каждый блок имеет частичную сумму из своих потоков,блочные суммы должны быть объединены.Это может быть сделано либо атомарно одним потоком из каждого блока, вторым вызовом ядра (с одним блоком), либо на хосте.То, как и где вы будете использовать окончательную сумму, определит, какой из этих вариантов является наиболее оптимальным для вашего приложения.

...