• Nem Talált Eredményt

2. Az OpenCL API eszközei

7.11. példa - memory.c:56-63

user@home> ./memory 5 6 7 8 9

A példaprogramban egy egyszerű, egy eszközből álló környezetben létrehozunk egy memóriaobjektumot, feltöltünk oda egy egészekből álló számsort, majd a feltöltött tömb második felét az output tömbbe olvassuk.

Mind az olvasási, mind az írási műveletet blokkoló műveletként hajtjuk végre, ami azt jelenti, hogy a program futása az adott ponton blokkolódik a művelet befejeződéséig. Ha nem blokkoló módon valósítjuk meg az írást és olvasást (ahogy azt korábban, más technológiáknál már láthattuk, a nem-blokkoló műveletek általában hatékonyabbak), akkor a függvények által visszaadott esemény objektumokat használhatjuk fel szinkronizációra. A példaprogramot az alábbi módon módosítva azonos működést kapunk, azonban az esemény objektumok gondoskodnak róla, hogy a megfelelő műveletek biztosan egymás után történjenek.

7.11. példa - memory.c:56-63

err= clEnqueueWriteBuffer(queue, memobj, 0, 0, sizeof(int)*ARRAY_SIZE, input, NULL, NULL, &event);

ERROR(err, "clEnqueueWriteBuffer");

err= clEnqueueReadBuffer(queue, memobj, 0, sizeof(int)*(ARRAY_SIZE/2), sizeof(int)*(ARRAY_SIZE/2), output, 1, &event, NULL);

ERROR(err, "clEnqueueReadBuffer");

err= clFinish(queue);

A memóriakezelő függvényeket most nem-blokkoló módon használjuk, ennek megfelelően harmadik paraméterük logikai hamis, azaz 0. Arról, hogy a memóriaterület írása biztosan megelőzze az olvasást, a clEnqueueReadBuffer függvénynek átadott esemény objektummal gondoskodunk, azt pedig, hogy a kimenetre írás biztosan az olvasás után történjen, a clFinish függvény explicit hívásával biztosítjuk.

Program objektumok. Áttekintettük eddig az OpenCL eszközrendszer inicializálásának lépéseit és a legegyszerűbb memóriakezelő függvények használatát. A párhuzamos végrehajtás felé vezető következő lépés a párhuzamosan végrehajtható kernelek létrehozása. A kernel programok írására használható OpenCL C nyelvvel a következő szakaszban foglalkozunk részletesebben. Jelen szakasz hátralévő részében az OpenCL C nyelvű programok fordításának és futtatásának lépéseit vizsgáljuk.

Az OpenCL C nyelvű forráskódokat és az OpenCL eszközre lefordított gépi kódot un. program objektumok formájában kezeljük. Program objektumot OpenCL C nyelvű forráskódból a clCreateProgramWithSource függvénnyel hozhatunk létre.

Specifikáció:

cl_program clCreateProgramWithSource(

cl_context context,

cl_uint count,

const char** strings,

const size_t* lengths,

cl_int* errcode_ret);

Paraméterek: context - A párhuzamos végrehajtás alapjául szolgáló környezet.

count - A strings tömb mérete.

strings - Az OpenCL C nyelvű forráskódot tartalmazó karaktertömbök mutatói.

lengths - A strings karaktertömbök hosszai.

errcode_ret - Hiba esetén a hibakód ezen címre kerül beállításra.

Visszatérési érték: Sikeres végrehajtás esetén egy cl_program objektum, ellenkező esetben beállításra kerül a hibakód.

Az OpenCL C nyelvű forráskód egy lépésben történő fordítására és linkelésére használhatjuk a clBuildProgram függvényt. Megjegyezzük, hogy a fordítás és linkelés két lépésben, külön függvények segítségével is elvégezhető (clCompileProgram, clLinkProgram), használatukra azonban csak összetett esetekben van szükség, ezért azt nem részletezzük.

7.5. táblázat - A legfontosabb OpenCL C fordítási és linkelési kapcsolók.

Kapcsoló Leírás

-D name Az előfeldolgozó definiálja a name konstanst 1 értékkel, használata teljesen analóg a GCC megfelelő kapcsolóinak használatával.

-D name=definition Az előfeldolgozó definiálja a name makrót definition értékkel.

-I dir Hozzáadja a dir könyvtárat azon listához, amelyekben az előfordító a #include <.> direktíva segítségével megadott header fájlokat keresi.

-cl-single-precision-constant A dupla pontosságú lebegőpontos konstansok egyszeres pontosságúként kezelése.

-cl-opt-disable Mindennemű optimalziálás kikapcsolása.

-cl-mad-enable Az a*b+c alakú konstrukciók helyettesítése a mad művelettel. Az eredmény gyorsabb kód lesz, de veszítünk a számértékek pontosságából.

-cl-no-signed-zeros Azt jelezhetjük vele, hogy a 0.0 érték előjelére nem építünk, ami kihasználható egyes optimalizálási műveleteknél.

-cl-unsafe-math-optimizations A pontosságot és a megfelelő IEEE szabványokat figyelmen kívül hagyó optimalizálási lehetőségeket kapcsolhatjuk be (magában foglalja a -cl-mad-enable és cl-no-signed-zeros kapcsolókat is).

-cl-finite-math-only A lebegőpontos aritmetikai műveletek optimalizálása, azzal a feltétellel, hogy biztosan nem jelennek meg NaN és inf értékek a program futása során.

-cl-std= Meghatározhatjuk vele az OpenCL C nyelv verzióját.

Lehetséges értékei: CL1.1 vagy CL1.2. Alapértelmezése az eszköz által támogatott OpenCL C

Kapcsoló Leírás

verzió, amelyet a clGetDeviceInfo függvénnyel le is kérdezhetünk.

-create-library Bekapcsolásával programkönyvtár készül a megadott programból.

Specifikáció:

cl_int clBuildProgram( cl_program program, cl_unit num_devices, const cl_device_id*

device_list,

const char* options, void (CL_CALLBACK*

pfn_notify)(cl_program program, void*

user_data),

void* user_data);

Paraméterek: program - Egy OpenCL C forráskódot tartalmazó program objektum.

num_devices - A device_list tömb elemeinek száma.

device_list - num_devices darab OpenCL eszköz azonosítóját tartalmazó tömb.

options - A fordítás és linkelés paramétereit tartalmazó karaktertömb.

pfn_notify - Egy alkalmas specifikációjú függvény, amelyet a clBuildProgram függvény akkor hív meg, amikor a fordítás és linkelés befejeződött. NULL paraméter esetén a clBuildProgram függvény blokkolja a program futását a fordítási folyamat befejeződéséig, nem NULL paraméter esetén nem blokkolja, a fordítás befejeztéről a pfn_notify függvényen keresztül értesülhetünk.

user_data - Ezen paramétert kapja meg a pfn_notify függvény második paramétereként.

Visszatérési érték: Sikeres végrehajtás esetén egy CL_SUCCESS, ellenkező esetben hibakód.

Ahogy az a függvény paramétereit áttekintve látható, a fordítás aszinkron módon is végrehajtható, ami gyorsíthatja a program végrehajtását. A clBuildProgram a paraméterként kapott eszközök mindegyikére elkészíti a megfelelő futtatható kódot, amely bekerül a szintén paraméterként kapott cl_program típusú program objektumba, num_devices eszköz esetén ez num_devices darab lefordított programot jelent. A fordítás és linkelés menetét az OpenCL C nyelvű forráskódok esetén is kapcsolókkal befolyásolhatjuk. A legfontosabb kapcsolókat a 7.5. táblázatban foglaljuk össze.

Ahogy azt a korábbiakban láthattuk, az OpenCL minden objektumhoz specifikál olyan függvényt, amellyel annak tulajdonságait kérdezhetjük le.

Program objektumok esetén ez a clGetProgramInfo.

7.6. táblázat - A program objektumok

clGetProgramInfo

függvénnyel lekérdezhető fontosabb tulajdonságai.

cl_program_info értéke Típus Leírás

CL_PROGRAM_CONTEXT cl_context A program objektum környezete.

CL_PROGRAM_NUM_DEVICES cl_uint A program objektumhoz rendelt OpenCL eszközök száma.

cl_program_info értéke Típus Leírás

CL_PROGRAM_DEVICES cl_device_id* A program objektumhoz rendelt OpenCL eszközök azonosítói.

CL_PROGRAM_SOURCE char* Az OpenCL C nyelvű forráskódja

egyetlen sztringként.

CL_PROGRAM_BINARY_SIZES size_t* A lefordított kódok méreteit tartalmazó tömb.

CL_PROGRAM_BINARIES char** A lefordított forráskódok.

CL_PROGRAM_NUM_KERNELS size_t A programban definiált kernelek száma.

CL_PROGRAM_KERNEL_NAMES char* A programban definiált kernelek nevei, pontosvesszővel elválasztva.

Specifikáció:

cl_int clGetProgramInfo( cl_program program,

cl_context_info param_name,

size_t param_value_size,

void* param_value, size_t*

param_value_size_ret);

Paraméterek: program - Egy program objektum.

param_name - A fontosabb lekérdezhető

tulajdonságokat a 7.6. táblázatban foglaljuk össze.

param_value_size - A függvény által param_value címre visszaadható bájtok száma.

param_value - Egy param_value_size bájt méretű lefoglalt memóriaterület címe.

param_value_size_ret - Ezen címre kerül be a ténylegesen visszaadott, azaz param_value címre másolt bájtok száma.

Visszatérési érték: Sikeres végrehajtás esetén CL_SUCCESS, ellenkező esetben hibakód.

A clGetProgramInfo függvény egyik leggyakoribb felhasználási módja az, amikor a lefordított kódokat egy bináris karakterfolyamként kérdezzük le. A kapott bináris kód fájlba írható és a későbbiekben a fájlból bármikor beolvasható, így ezzel megspórolhatjuk azt, hogy a párhuzamos végrehajtás előtt minden alkalommal le kelljen fordítani az esetleg igen nagy és összetett kernel kódot.

Program objektumokat korábban lefordított bináris kódokból a clCreateProgramWithBinary függvénnyel hozhatunk létre.

Specifikáció:

cl_program clCreateProgramWithBinary(

cl_context context,

cl_uint num_devices,

const cl_device_id* device_list,

const size_t* lengths,

const unsigned char** binaries,

cl_int* binary_status,

cl_int* errcode_ret);

Paraméterek: context - A megfelelő környezet objektum.

num_devices - A device_list tömb elemeinek száma.

device_list - Azon eszköz azonosítóinak tömbje, amelyekre lefordított kernel-kóddal rendelkezünk.

lengths - A bináris programkódot tartalmazó tömbök hosszainak num_devices méretű tömbje.

binaries - Bináris programkódok mutatóinak num_devices elemszámú tömbje. A paraméterek megadása során arra kell ügyelnünk, hogy a binaries[i] bináris kód hossza lengths[i] és a devices[i] azonosítójú eszközre lett lefordítva.

binary_status - num_devices elemszámú tömb, amelynek i. elemére a CL_SUCCESS értéket állítja be a függvény, ha az i. kód betöltése sikeresen megtörtént.

errcode_ret - Sikertelen végrehajtás esetén ezen címre kerül beállításra a hibakód.

Visszatérési érték: Sikeres végrehajtás esetén egy cl_program objektum és az errcode_ret értéke CL_SUCCESS, ellenkező esetben beállításra kerül a hibakód.

A lefordított, bináris kódok kezelésénél arra kell ügyelnünk, hogy a különböző eszközöknek különbözik az utasításkészlete, ezért több OpenCL eszköz használata esetén több különböző bináris kóddal kell dolgoznunk.

Akár forráskódból, akár bináris kódból hoztuk létre a program objektumot, a hozzá rendelt erőforrásokat a korábbiakhoz hasonló clReleaseProgram függvénnyel szabadíthatjuk fel.

Az alábbi példaprogramban kernelek forráskódjának olvasására/írására, fordítására, valamint bináris kódok olvasására és írására alkalmas függvények használatát szemléltetjük.

A kernel kódok olvasását most és a fejezet hátralévő részében a kernelio.h és kernelio.c fájlokban definiált függvények segítségével végezzük.