Toshkent axborot texnologiyalari universiteti


OpenCL interfeysi asosida ikki o‘lchovli singallarga parallel ishlov berish va natijalar tahlili



Yüklə 4,21 Mb.
səhifə16/18
tarix27.05.2023
ölçüsü4,21 Mb.
#113620
1   ...   10   11   12   13   14   15   16   17   18
Tayyor Dissertatsiya ishi Abdusalomov Saidmalikxon

3.3. OpenCL interfeysi asosida ikki o‘lchovli singallarga parallel ishlov berish va natijalar tahlili


OpenCl yordamida bir, ikki va hatto uch o‘lchovli signallarga ishlov berish imkoniyati mavjud. Biz uni CUDA dasturi bilan taqqoslash maqsadida 2 o‘lchamli signallarni qayta ishlash vazifasida sinab ko‘ramiz.

3.18- rasm. Bir, ikki va uch o‘lchovli tasvirlash
OpenCL dasturida CUDA dan farqli o‘laroq faqat Nvidia kompaniyasi GPU larinigina emas balki istalgan GPU yoki boshqa protsessorlarni ishlatish imkoniyati bo‘lganiligi sababli, boshqa konfiguratsiyada tajribalar amalga oshirildi.
CPU: Ryzen 5 5500u 6 cores 12 threads 2.2-4.2mhz
RAM: 8gb 2666mhz DDR4
GPU : AMD Radeon Vega 7 2gb
OpenCL 3.0
OS: Windows 10 pro 64 bit
OpenCL ko‘plab qurilmalar bilan integratsiya bo‘lishi sababli, hisoblashlar qaysi qurilmada olinsa uning ID ni topish drayverlarini ko‘rish CUDAga nisbatan qiyinroq bo‘ladi. OpenCLda dasturlash ham huddi CUDA kabi bo‘ladi, ya’ni birinchi CPU da tasvirga haar veyvlet o‘zgaritish berib, keyin esa GPU orqali ushbu tajriba qayta takrorlanadi va natijalar taqqoslanadi. Ikkaslining ham ishlash prinsipi o‘xshash hisoblash kerak bo‘lgan ma`lumotlar qurilma yadrolariga teng taqsimlanadi, har bir yadroda hisoblash tugaguncha kutib turiladi, shina orqali natijalar asosiy xotiraga jamlanadi va ekranga natijalar hamda uni bajarish uchun ketgan vaqt kabi ma`lumotlar chiqadi[15].
Haar to‘lqinli transformatsiyasi va OpenCL dasturlash tili yordamida tasvirni qayta ishlashga quyidagicha erishish mumkin:
OpenCL muhitini tayyorlashdan boshlaymiz. OpenCL kontekstini yaratib, hisob-kitoblarni amalga oshirish uchun qurilmani tanlanadi va buyrug‘lar navbatini yaratamiz. Asl tasvirni qurilma xotirasiga yuklanadi. Qurilma xotirasida 2d tasvirni yaratish uchun clCreateImage2D funktsiyasidan foydalanish mumkin. Tasvirdagi haar veyvlet o‘zgarishini amalga oshirish uchun OpenCL yadrosini kiritamiz. Yadro turli darajadagi qismlar va yaqinlashuvlarda parchalanish va tiklash bosqichlarini o‘z ichiga olishi kerak. Clcreateprogramwithsource, clBuildProgram va clCreateKernel funktsiyalari yordamida OpenCL yadrosi qurilmada kompilyatsiya qilinadi va yuklanadi. Konvertatsiya natijalarini saqlash uchun qurilmaning xotira buferlarini yaratib va ularni OpenCL yadrosiga bog‘lanaymiz. OpenCL yadrosi argumentlarini, shu jumladan kirish va chiqish tasvirlarini, shuningdek konversiyani amalga oshirish uchun zarur bo‘lgan o‘lchamlarni va boshqa parametrlarni o‘rnating. Buyruqlar navbati yordamida qurilmada OpenCL yadrosini ishga tushiramiz va qurilma xotirasidan natijalarni olish uchun clEnqueueReadBuffer bilan tugashini kutamiz. Transformatsiya natijalarini qayta ishlaymiz, masalan, kontrastni oshirish yoki shovqinni filtrlash kabi turli xil operatsiyalarni qo‘llaymiz va o‘zgartirilgan tasvirni saqlaymiz. OpenCL resurslarini, shu jumladan xotira buferlari, yadro va kontekstni tegishli xotirani bo‘shatamiz. Quyida OpenCL texnologiyasida dasturlashning blok sxemasi keltirilgan[19].



3.19- rasm. OpenCL yordamida dasturlashning blok sxemasi
OpenCl yordamida GPU da ikki o‘lchovli signallarga ishlov berish bir o‘lchovli signallarga ishlov berish kabi bajariladi. Biroq GPU yadrolarigalariga ikki o‘lchovli massivni bir o‘lchovli massivga aylantirib uzatamiz. Bu aylantish dasturi quyidagicha bo‘ladi.

Open CLda dasturlash


void HWTf(double** data, int iterations, int h, int w)


int rows = h;
int cols = w;
double *row = new double[cols];
double *col = new double[rows];
for (int k = 0; k < iterations; k++)
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
{
row[j] = data[i][j];
}
HWTf(row, cols);
for (int j = 0; j < cols; j++)
{
data[i][j] = row[j];
}
}
for (int j = 0; j < cols; j++)
{
for (int i = 0; i < rows; i++)
{
col[i] = data[i][j];
}
HWTf(col, rows);
for (int i = 0; i < rows; i++)
{
data[i][j] = col[i];
}
}
}
}
void HWTr(double** data, int iterations, int h, int w)
int rows = h;
int cols = w;
double *col = new double[rows];
double *row = new double[cols];
for (int k = 0; k < iterations; k++)
{
for (int j = 0; j < cols; j++)
for (int i = 0; i < rows; i++)
{
col[i] = data[i][j];
}
HWTr(col, rows);
for (int i = 0; i < rows; i++)
{
data[i][j] = col[i];
}
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
row[j] = data[i][j];
}
HWTr(row, cols);
for (int j = 0; j < cols; j++)
{
data[i][j] = row[j];
}
}
}
}
Bu dasturdagi HWTf va HWTr funksiyalari data[ ][ ] ikki o‘lchovli massivni
row[ ] va col[ ] nomli bir o‘lchovli massiv ko‘rinishiga keltirib beradi.
Endi esa OpenCl orqali 2 o‘lchamli signalga parallel ishlov berishni ko‘rib chiqamiz
#include "stdio.h"
#include
#include
#include
#include
#include
#include
#include
#include "omp.h"
using namespace std;

#define MAX_SOURCE_SIZE (0x1000000)


int main() {
srand(time(0));
// Ma`lumotlarni uzatish va qabul qilish uchun 2ta vector hosil qilish
int i;
int LIST_SIZE =1024;
float *s_arr = (float*)malloc(sizeof(float)*LIST_SIZE);
float *r_arr = (float*)malloc(sizeof(float)*LIST_SIZE);
for(i = 0; i < LIST_SIZE; i++)
{
s_arr[i] = rand() % 10000 + 1;
r_arr[i] = 0;
// Dastur kodini massivdan yuklash source_str
FILE *fp;
char *source_str;
size_t source_size;
fp = fopen("hello.cl", "r");
if (!fp) {
fprintf(stderr, "Failed to load kernel.\n");
exit(1);
}
source_str = (char*)malloc(MAX_SOURCE_SIZE);
source_size = fread( source_str, 1, MAX_SOURCE_SIZE, fp);
fclose( fp );
// Platforma va qurilma haqida ma`lumotlarni o‘qish
cl_platform_id platform_id = NULL;
cl_device_id device_id = NULL;
cl_uint ret_num_devices;
cl_uint ret_num_platforms;
cl_int ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
ret = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, &ret_num_devices);
// OpenClda konteskt hosil qilish
cl_context context = clCreateContext( NULL, 1, &device_id, NULL, NULL, &ret);
// Vazifalar ketma-ketligini hosil qilish (command queue)
cl_command_queue command_queue = clCreateCommandQueue(context, device_id, 0, &ret);
// Har bir vector uchun qurilma xotirasida buffer yaratish
cl_mem s_mem_obj = clCreateBuffer(context, CL_MEM_READ_ONLY, LIST_SIZE * sizeof(float), NULL, &ret);
cl_mem r_mem_obj = clCreateBuffer(context, CL_MEM_WRITE_ONLY, LIST_SIZE * sizeof(float), NULL, &ret);
// S_arr tarkibini mos keladigan qurilma buferiga nusxalash
ret = clEnqueueWriteBuffer(command_queue, s_mem_obj, CL_TRUE, 0, LIST_SIZE * sizeof(float), s_arr, 0, NULL, NULL)
// Yadro manbalaridan dastur yaratish
cl_program program = clCreateProgramWithSource(context, 1, (const char **)&source_str, (const size_t *)&source_size, &ret);
// Dastur qurish
ret = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
// OpenCl yadrosini hosil qilish
cl_kernel kernel = clCreateKernel(program, "HWT_gpu", &ret);
// Yadrolar parametrlarini sozlash
ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), &s_mem_obj);
ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), &r_mem_obj);
ret = clSetKernelArg(kernel, 2, sizeof(int), &LIST_SIZE);
// Vektor uchun OpenCl yadrosini yaratish
size_t global_size = LIST_SIZE;
size_t local_size = 2048; //
// GPU uchun
double GPU_t1, GPU_t2;
GPU_t1 = omp_get_wtime();
ret = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, &global_size, &local_size, 0, NULL, NULL);
// Qurilmaning xotira buferi tarkibini kompyuter xotirasiga nusxalash
ret = clEnqueueReadBuffer(command_queue, r_mem_obj, CL_TRUE, 0, LIST_SIZE * sizeof(float), r_arr, 0, NULL, NULL);
GPU_t2 = omp_get_wtime();
// Ketma-ket versiya
double CPU_t1, CPU_t2;
CPU_t1 = omp_get_wtime();
int n = LIST_SIZE >> 1;
for(i = 0; i < n; i++)
{
int k = (i << 1);
r_arr[k] = (s_arr[k] + s_arr[k+1])/2;
r_arr[k+1] = (s_arr[k] - s_arr[k+1])/2;
}
CPU_t2 = omp_get_wtime();
// Natijalarni ekranga chiqarish
for(i = 0; i < LIST_SIZE; i++)
printf("%d. %f : %f\n", i, s_arr[i], r_arr[i]);
cout << "Time for GPU: " <<(GPU_t2 - GPU_t1) << endl;
cout << "Time for CPU: " << (CPU_t2 - CPU_t1) << endl;
// tozalash
ret = clFlush(command_queue);
ret = clFinish(command_queue);
ret = clReleaseKernel(kernel);
ret = clReleaseProgram(program);
ret = clReleaseMemObject(s_mem_obj);
ret = clReleaseMemObject(r_mem_obj);
ret = clReleaseCommandQueue(command_queue);
ret = clReleaseContext(context);
free(s_arr);
free(r_arr);
system("pause");
return 0;}
Natija.cl fayliga quyidagi kodni yozamiz
__kernel void HWT_gpu(__global float *A, __global float *B,
const int LIST_SIZE)
//Qayta ishlash uchun joriy element indeksini olish
int i = get_global_id(0);
int k=0, n = LIST_SIZE >> 1;
if (i < n)
k = (i << 1);
B[k] = (A[k] + A[k+1])/2;
B[k+1] = (A[k] - A[k+1])/2;
}
}

3.20- rasm. OpenCL dasturida tasvirga ishlov berish natijalari
Tasvirda dastlab shumni olib tashlash amali bajarildi, keyin esa uning kontrasti eng maksimal darajagacha ko‘tarildi. Bu holat ikkala protsessorda ham bir xil natija berdi faqat uni bajarish uchun ketgan vaqt bir biridan tasvirning hajmi va formatiga qarab farq qildi.
Olingan natijalar shuni ko‘rsatib turibdiki, har qanday holatda ham GPUni qo‘llash o‘rinli emas, chunki ma`lumotlarni GPU ga uzatish va natijalarni qabul qilish uchun ketadigan vaqtning o‘rniga yengilroq vazifani CPUning o‘zida bajarib qo‘yish, samaraliroq bo‘lishi mumkin bo‘lgan holatlar mavjud. Ma`lumotning hajmi qanchalik oshib borgani sari GPU ning samaradorlik ko‘rsatkichi oshib boradi.
3.2-jadval
Tasvirga ishlov berish uchun CPU va GPUda ketgan vaqt

Piksellar Soni

Vazifani bajarish uchun ketgan vaqt(sek)

CPU

GPU

128ta

0,23

1,27

512ta

0,54

1,39

1024ta

0,92

1,43

2048ta

1,77

1,65

4096ta

3,46

1,84

8192ta

7,13

1,98

16384ta

13,37

2,23




3.18- rasm. Tarjiba natijalari grafik ko‘rinishda

Yüklə 4,21 Mb.

Dostları ilə paylaş:
1   ...   10   11   12   13   14   15   16   17   18




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə