Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <cstdlib>
- const int REPEAT_TEST = 10;
- const int TRANSF_ITER = 100;
- const int BLOCKS [] = { 2, 4, 8, 16, 32}; // проверки на соразмерность масивов нет!
- const int THREADS_PER_BLOCK [] = {512, 256, 128, 64, 32};
- const int THREADS_PER_BLOCK_max = 512;
- __device__ volatile unsigned int count; //volatile
- __device__ void do_while_count_not_eq(int val)
- {
- /*
- // Цикл задержки в виде ассемблерной вставки. Точно будет в коде, не требует "volatile"-переменной count.
- asm("{\n\t"
- "$my_while_label: \n\t"
- " .reg .u32 r_count; \n\t"
- " .reg .pred p; \n\t"
- " ld.global.u32 r_count, [count]; \n\t"
- " setp.ne.u32 p, r_count, %0; \n\t"
- "@p bra $my_while_label; \n\t"
- "}\n\t"
- : : "r"(val));
- */
- // Цикл задержки просто на Си. Компилятор nvcc "выкинет" его, если переменная count не определена с использованием "volatile".
- while (count != val);
- }
- /* Фунция начальной инициализации флага-счетчика: */
- __device__ void InitSyncWholeDevice(const int index)
- {
- if (index == 0) // Первый поток в grid`е запишет нулем (начальным значением) флаг-счетчик блоков.
- count = 0;
- if (threadIdx.x == 0) // Первый поток каждого block`а будет ждать, пока флаг-счетчик дейcтвительно станет нулем.
- do_while_count_not_eq(0);
- __syncthreads(); // Заставляем остальные потоки каждого block`а ждать, пока первые не выйдут из цикла.
- } // Все, флаг-аккумулятор записан. Все потоки на device более-менее идут вровень.
- /* Фунция синхронизации потоков на device: */
- __device__ void SyncWholeDevice()
- {
- unsigned int oldc; // Переменная под значение счетчика до инкремента.
- __threadfence(); // Каждый поток ждет, пока записанное им в gmem и smem, станет видно всему grid`у. __syncthreads отвечает только за видимость для block.
- if (threadIdx.x == 0) // Первые потоки каждого block`а атомарным образом инкрементируют (каждый по разу) флаг-аккумулятор.
- {
- oldc = atomicInc((unsigned int *)&count, gridDim.x-1); // В oldc кладется значение count до "+1".
- __threadfence(); // Пусть поток подождет, пока его инкремент "дойдет" до ячейки в gmem.
- if (oldc != (gridDim.x-1)) // Если это последний блок (остальные уже инкрементировали count и ждут за счет цикла ниже), то и незачем ему считывать count. Так как предварительно убедились, что его инкремент записан в gmem.
- do_while_count_not_eq(0); // Каждый первый поток в каждом block`е зациклен, пока все блоки не закончат работу.
- }
- __syncthreads(); // Заставляем потоки в каждом блоке ждать, пока первые не выйдут из цикла.
- }
- __device__ void SingleTransform(const float *src, float *dest)
- {
- float a = 0;
- /* Цикл вычисления элемента dest[]. В двух вариантах. */
- // Вариант №1: без использования разделяемой памяти:
- for(int j = 0; j < gridDim.x*blockDim.x; ++j)
- a += src[j];
- // Вариант №2: с использованием разделяемой памяти:
- /*__shared__ float smemBuff [THREADS_PER_BLOCK_max];
- for(int jb = 0; jb < gridDim.x; ++jb)
- {
- __syncthreads(); // Необходимо дождться, пока smemBuff[] больше не будет нужен никакому потоку из блока.
- smemBuff[threadIdx.x] = src[threadIdx.x + jb*blockDim.x];
- __syncthreads(); // Необходимо дождться, пока в smemBuff[] все потоки блока запишут значения.
- for(int jt = 0; jt < blockDim.x; ++jt)
- a += smemBuff[jt];
- }*/
- a /= (gridDim.x*blockDim.x);
- dest[threadIdx.x + blockIdx.x*blockDim.x] = a;
- }
- __global__ void Kernel_SKL(float *X, float *P)
- {
- InitSyncWholeDevice(threadIdx.x + blockIdx.x*blockDim.x);
- for (int i = 0; i < TRANSF_ITER; ++i)
- {
- SingleTransform(X, P);
- SyncWholeDevice();
- SingleTransform(P, X);
- SyncWholeDevice();
- }
- }
- __global__ void Kernel_MKL(float *src, float *dest)
- {
- SingleTransform(src, dest);
- }
- int main(void)
- {
- int NUM_OF_ELEMS;
- float *X_h;
- float *X_d, *P_d;
- float *res[REPEAT_TEST+1];
- float elapsedTime_MKL[2];
- float elapsedTime_SKL[2];
- cudaEvent_t start, stop;
- cudaEventCreate(&start);
- cudaEventCreate(&stop);
- FILE *fo = fopen("out.txt", "w");
- for(int jnb = 0; jnb < sizeof(BLOCKS)/sizeof(*BLOCKS); ++jnb)
- for(int jnt = 0; jnt < sizeof(THREADS_PER_BLOCK)/sizeof(*THREADS_PER_BLOCK); ++jnt)
- {
- printf("BLOCKS = %3i, THREADS_PER_BLOCK = %3i:\n", BLOCKS[jnb], THREADS_PER_BLOCK[jnt]);
- NUM_OF_ELEMS = BLOCKS[jnb] * THREADS_PER_BLOCK[jnt];
- X_h = new float [NUM_OF_ELEMS];
- cudaMalloc(&X_d, NUM_OF_ELEMS*sizeof(float));
- cudaMalloc(&P_d, NUM_OF_ELEMS*sizeof(float));
- srand(time(NULL));
- for(int j = 0; j < NUM_OF_ELEMS; ++j)
- X_h[j] = float(rand()) / RAND_MAX; // как вариант: float(j+1)/NUM_OF_ELEMS;
- for(int r = 0; r <= REPEAT_TEST; ++r)
- res[r] = new float [NUM_OF_ELEMS];
- elapsedTime_MKL[0] = elapsedTime_SKL[0] = 0;
- for(int r = 1; r <= REPEAT_TEST; ++r)
- {
- printf(" r = %i/%i\r", r, REPEAT_TEST);
- fflush(stdout);
- cudaMemcpy(X_d, X_h, NUM_OF_ELEMS*sizeof(float), cudaMemcpyHostToDevice);
- cudaEventRecord(start, 0);
- for (int i = 0; i < TRANSF_ITER; ++i)
- {
- Kernel_MKL <<<BLOCKS[jnb], THREADS_PER_BLOCK[jnt]>>> (X_d, P_d);
- Kernel_MKL <<<BLOCKS[jnb], THREADS_PER_BLOCK[jnt]>>> (P_d, X_d);
- }
- cudaEventRecord(stop, 0);
- cudaEventSynchronize(stop);
- cudaEventElapsedTime(&elapsedTime_MKL[1], start, stop);
- elapsedTime_MKL[0] += elapsedTime_MKL[1];
- cudaMemcpy(res[0], X_d, NUM_OF_ELEMS*sizeof(float), cudaMemcpyDeviceToHost); // Дабы не усложнять пример, лишние несколько раз скопируем одно и то же в res[0].
- cudaMemcpy(X_d, X_h, NUM_OF_ELEMS*sizeof(float), cudaMemcpyHostToDevice);
- cudaEventRecord(start, 0);
- Kernel_SKL <<<BLOCKS[jnb], THREADS_PER_BLOCK[jnt]>>> (X_d, P_d);
- cudaEventRecord(stop, 0);
- cudaEventSynchronize(stop);
- cudaEventElapsedTime(&elapsedTime_SKL[1], start, stop);
- elapsedTime_SKL[0] += elapsedTime_SKL[1];
- cudaMemcpy(res[r], X_d, NUM_OF_ELEMS*sizeof(float), cudaMemcpyDeviceToHost);
- }
- printf(" Elapsed Time\n");
- printf(" multi kerenel launch: %20.14f\n", elapsedTime_MKL[0]/REPEAT_TEST);
- printf(" single kerenel launch: %20.14f\n", elapsedTime_SKL[0]/REPEAT_TEST);
- fprintf(fo, "%i %i %20.14f %20.14f\n", jnb, jnt, elapsedTime_MKL[0]/REPEAT_TEST, elapsedTime_SKL[0]/REPEAT_TEST);
- // Сравним между собой все j-е элементы из каждой r-ой реализации. Выявит численную ошибку за счет использования неактуальных значений элементов массивов.
- for(int j = 0; j < NUM_OF_ELEMS; ++j)
- {
- int r;
- float e = res[0][j];
- for(r = 1; r < REPEAT_TEST; ++r)
- if (e != res[r][j])
- {
- printf(" Numeric error of SKL-method has been detected!\n");
- break;
- }
- if (e != res[r][j])
- break;
- }
- cudaFree(X_d);
- cudaFree(P_d);
- delete[] X_h;
- for(int r = 0; r < (REPEAT_TEST+1); ++r)
- delete[] res[r];
- }
- fclose(fo);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement