CUDA распараллеливание вложенного цикла for
Я новичок в CUDA. Я пытаюсь распараллелить следующий код. Прямо сейчас он сидит на ядре, но не использует потоки вообще, таким образом, медленно. Я пытался использовать это ответ но пока безрезультатно.
предполагается, что ядро генерирует первые n простых чисел, помещает их в массив device_primes, и этот массив позже доступен с хоста. Код правильный и отлично работает в серийной версии, но мне нужно ускорить его, возможно, с использованием общей памяти.
//CUDA kernel code
__global__ void generatePrimes(int* device_primes, int n)
{
//int i = blockIdx.x * blockDim.x + threadIdx.x;
//int j = blockIdx.y * blockDim.y + threadIdx.y;
int counter = 0;
int c = 0;
for (int num = 2; counter < n; num++)
{
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
{
break;
}
}
if (c == num) //prime
{
device_primes[counter] = num;
counter++;
}
}
}
моя текущая, предварительная и определенно неправильная попытка распараллелить это выглядит следующим образом:
//CUDA kernel code
__global__ void generatePrimes(int* device_primes, int n)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
int num = i + 2;
int c = j + 2;
int counter = 0;
if ((counter >= n) || (c > num - 1))
{
return;
}
if (num % c == 0) //not prime
{
}
if (c == num) //prime
{
device_primes[counter] = num;
counter++;
}
num++;
c++;
}
но этот код заполняет массив данными, которые не имеют смысла. Кроме того, многие значения являются нулями. Заранее спасибо за любую помощь, это ценится.
1 ответов
у вас есть некоторые проблемы в коде, например:
int num = i + 2;
предоставление потоку 0 взаимодействия 2, потоку 1 итерации 3 и так далее. Проблема в том, что следующая итерация, которую будут вычислять потоки, основана на num++;. Таким образом, это означает, что поток 0 выполнит следующую итерацию 3, уже выполненную потоком 1. Поэтому у вас будут избыточные вычисления. Кроме того,я думаю, что для этой проблемы будет проще использовать только одно измерение вместо 2 (x, y). Таким образом, основываясь в этом предположении вы должны изменить num++ для:
num += blockDim.x * gridDim.x;
другая проблема заключается в том, что вы не приняли во внимание, что счетчик переменных должен быть разделен между потоками. В противном случае каждый поток попытается найти " n " простых чисел, и все они будут заполнять весь массив. Поэтому вам нужно изменить int counter = 0; для общей или глобальной переменной мы будем использовать глобальную переменную, чтобы она была видна среди всех потоков из всех блоков. Мы можем использовать нулевую позицию массива device_primes держать счетчик.
плюс, вы должны инициализировать это значение, вы дадите это задание только одному потоку. Давайте дадим это задание потоку с id = 0, так что:
if (thread_id == 0) device_primes[0] = 1;
но поскольку эта переменная является глобальной и будет записываться всеми потоками, вы должны гарантировать, что все потоки, прежде чем писать на ней, увидят, что счетчик равен 1 (первая позиция device_primes с простыми числами, ноль для счетчика), поэтому вам нужно добавить также барьер в конце, Итак:
if (thread_id == 0) device_primes[0] = 1;
__syncthreads()
таким образом, возможное решение (неэффективное):
__global__ void getPrimes(int *device_primes,int n)
{
int c = 0;
int thread_id = blockIdx.x * blockDim.x + threadIdx.x;
int num = thread_id;
if (thread_id == 0) device_primes[0] = 1;
__syncthreads();
while(device_primes[0] < n)
{
for (c = 2; c <= num - 1; c++)
{
if (num % c == 0) //not prime
{
break;
}
}
if (c == num) //prime
{
int pos = atomicAdd(&device_primes[0],1);
device_primes[pos] = num;
}
num += blockDim.x * gridDim.x; // Next number for this thread
}
}
следующая строка atomicAdd (&device_primes[0], 1); будет в основном делать device_primes[0]++; но поскольку counter является глобальным, вы должны гарантировать взаимное исключение. Вот почему я использовал эту атомную операцию. Обратите внимание, что вам может потребоваться компиляция с флагом-arch sm_20.
оптимизация: С точки зрения кода предпочтительнее подход с меньшей синхронизацией/без синхронизации. Также вы можете уменьшить количество вычислений, принимая во внимание некоторые из приличий простых чисел, как вы можете видеть вhttp://en.wikipedia.org/wiki/Sieve_of_Eratosthenes.