| alusta \ valmistaja | Nvidia | AMD | Intel |
|---|---|---|---|
| OpenCL 1.2 | Tuettu | Tuettu | Tuettu |
| OpenCL 2.0 | Ei tuettu | Tuettu | Rajoitettu tuki |
| CUDA | Tuettu | Ei tuettu | Ei tuettu |


__global)__local)__constant)(__private)CL/cl.hpp -otsikkotiedostonmain.cpp), jossa tarkistetaan käytettyjen parametrien sopivuus käytetylle OpenCL-laitteella ja käsitellään kaikki virhetilanteetmain.cpp-tiedosto alkaa CL/cl.hpp -otsikkotiedoston sisällyttämisellä:#include <CL/cl.hpp>
cl-nimiavaruuteenusing namespace cl -komentoa. Otamme sen kuitankin mukaan tässä esimerkissä selvyyden vuoksi.VECTOR_CLASS ja STRING_CLASS viittaavat oletuksena std::vector ja std::string tietotyyppeihincl_int-tyyppisen virhekoodin taicl_int-tyyppiseen virhekoodimuuttujaan, johon OpenCL-kutsun status kirjoitetaanCL_SUCCESS (0) on geneerinen kaikki ok -paluuarvoCL_DEVICE_NOT_FOUND / -1,CL_OUT_OF_RESOURCES / -5,CL_INVALID_COMMAND_QUEUE / -37,CL_INVALID_KERNEL_ARGS / -52CL/cl.h-tiedostosta#define __CL_ENABLE_EXCEPTIONS
#include <CL/cl.hpp>
try-catch-rakenteella:try {
...
} catch(const cl::Error &err) {
std::cerr << "Virheviesti: " << err.what() << std::endl;
}
cl::Platform -luokan sisälle, joka tarjoaa staattisen jäsenfunktion OpenCL-alustojen hakemiseen:static cl_int cl::Platform::get(VECTOR_CLASS<Platform> * platforms)
cl_int err;
std::vector<cl::Platform> platforms;
err = cl::Platform::get(&platforms);
if(err != CL_SUCCESS) {
std::cerr << "OpenCL-alustojen hakeminen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
if(platforms.size() < 1) {
std::cerr << "OpenCL-alustoja ei löytynyt." << std::endl;
return 1;
}

getInfo -jäsenfunktion avulla:cl_int cl::Platform::getInfo(cl_platform_info name,
STRING_CLASS * param)
CL_PLATFORM_VERSION -lipulla ja valmistajan nimen saa selville CL_PLATFORM_VENDOR - lipullaCL_PLATFORM_EXTENSIONS -lippu palauttaa listan tuetuista laajennuksista.cl::Device -luokan sisään ja cl::Platform -luokka sisältää getDevices-jäsenfunktion OpenCL-laitteiden kyselemistä varten:cl_int cl::Platform::getDevices(cl_device_type type,
VECTOR_CLASS<Device> * devices)
type -argumentti voi saada esimerkiksi arvot CL_DEVICE_TYPE_CPU, CL_DEVICE_TYPE_GPU tai CL_DEVICE_TYPE_ACCELERATOR. Tämän kurssin puitteissa CL_DEVICE_TYPE_GPU on tietenkin kaikkein mielenkiintoisin vaihtoehto.std::vector<cl::Device> devices;
err = platforms[0].getDevices(CL_DEVICE_TYPE_GPU, &devices);
if(err != CL_SUCCESS) {
std::cout << "OpenCL-laitteiden hakeminen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
if(devices.size() < 1) {
std::cout << "OpenCL-laitteita ei löytynyt." << std::endl;
return 1;
}

cl::Device -oliolta eri asioita:template <typename T>
cl_int cl::Device::getInfo(cl_device_info name,
T * param)
CL_DEVICE_MAX_WORK_GROUP_SIZE -lippu palauttaa säieryhmän suurimman mahdollisen koonCL_DEVICE_MAX_MEM_ALLOC_SIZE -lippu palauttaa suurimman varattavissa olevan globaalin muistialueen koonCL_DEVICE_LOCAL_MEM_SIZE-lippu palauttaa suurimman varattavissa olevan lokaalin muistialueen koonCL_DEVICE_EXTENSIONS -lippu palauttaa OpenCL-laitteen tukeman laajennuksetcl::Context -luokan sisään, jonka muodostinfunktio ottaa argumenttinaan mukaan liitettävät OpenCL-laiteet:cl::Context::Context(VECTOR_CLASS<Device>& devices,
cl_context_properties * properties = NULL,
void (CL_CALLBACK * pfn_notify)(
const char * errorinfo,
const void * private_info,
::size_t cb,
void * user_data) = NULL,
void * user_data = NULL,
cl_int * err = NULL)
std::vector<cl::Device> oneDevice;
oneDevice.push_back(devices[0]);
cl::Context context(oneDevice, 0, 0, 0, &err);
if(err != CL_SUCCESS) {
std::cout << "OpenCL-kontekstin luominen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}

cl::Context -olio voidaan luoda myöskin ilman cl::Device -oliota:cl::Context::Context(cl_device_type type,
cl_context_properties * properties = NULL,
void (CL_CALLBACK * pfn_notify)(
const char * errorinfo,
const void * private_info,
::size_t cb,
void * user_data) = NULL,
void * user_data = NULL,
cl_int * err = NULL)
type argumenttia vastaavat laitetyypitgetInfo -jäsenfunktiolla:template <typename T>
cl_int cl::Context::getInfo(cl_context_info name,
T * param)
CL_CONTEXT_DEVICES -lippu, joka palauttaa kontekstiin liitetyt OpenCL-laiteet, on hyödyllinen mikäli cl::Context -olio luotiin ilman cl::Device -oliota.buffer) kokonaislukuja (n kpl) ja lisää jokaiseen alkioon luvun yksi:__kernel void add_one(__global int *buffer, int n) {
const int global_id = get_global_id(0);
if(global_id < n)
buffer[global_id]++;
}
__kernel -avainsana kertoo, että kyseessä on ydin, jota voidaan kutsua isäntäohjelman puolelta__global -avainsana kertoo, että buffer-taulukko on tallennettu globaaliin muistiinget_global_id -aliohjelman avulla. Aliohjelmalle annettu argumentti määrää indeksi dimension:const int global_id = get_global_id(0);
if(global_id < n)
buffer[global_id]++;
const char *kernel =
"__kernel void add_one(__global int *buffer, int n) { \n" \
" const int global_id = get_global_id(0); \n" \
" if(global_id < n) \n" \
" buffer[global_id]++; \n" \
"} \n";
cl::Program::Sources -tyyppisen olion sisälle:cl::Program::Sources sources;
sources.push_back(
cl::Program::Sources::value_type(kernel, strlen(kernel)));
xxd -työkalu kelpaa tähän tarkoitukseen loistavastikernel.cl -tiedostoon. Tällöin komento xxd -i kernel.cl > kernel.cl.dat tuottaisi seuraavan tiedoston:unsigned char kernel_cl[] = {
0x0a, 0x2f, 0x2f, 0x20, 0x59, 0x64, 0x69, 0x6e, 0x2c, ...
0x6b, 0x61, 0x20, 0x6f, 0x74, 0x74, 0x61, 0x61, 0x20, ...
...
0x2b, 0x3b, 0x20, 0x0a, 0x7d
};
unsigned int kernel_cl_len = 857;
#include "kernel.cl.dat"
...
cl::Program::Sources sources;
sources.push_back(cl::Program::Sources::value_type(
(const char*) kernel_cl, kernel_cl_len));
xxd -työkalun hyödyntämisen lisäksi ytimien lähdekoodi voitaisiin ladata erillisestä tekstitiedostosta ajonaikaisesti merkkijonotaulukkoncl::Program -luokan sisälle, jonka muodostinfunktio ottaa argumenttinaan liittyvän OpenCL-kontekstin ja ytimien lähdekoodit:cl::Program::Program(const Context& context,
const Sources& sources,
cl_int * err = NULL)
cl::Program program(context, sources, &err);
if(err != CL_SUCCESS) {
std::cout << "Ohjelma-objektin luominen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}

cl::Program -luokan build -jäsenfunktion avulla:cl_int cl::Program::build(const VECTOR_CLASS<Device>& devices,
const char * options = NULL,
void (CL_CALLBACK * pfn_notify)(
cl_program,
void * user_data) = NULL,
void * data = NULL)
CL_PROGRAM_BUILD_LOG -lippu ´getBuildInfo´-jäsenfunktiolle:template <typename T>
cl_int cl::Program::getBuildInfo(cl_program_build_info name,
T * param)
err = program.build(oneDevice, 0);
if(err != CL_SUCCESS) {
std::string log;
program.getBuildInfo(oneDevice[0],
CL_PROGRAM_BUILD_LOG, &log);
std::cout <<
"OpenCL-kääntäjän tuloste:" << std::endl <<
log << std::endl;
std::cout <<
"Ytimien lähdekoodin kääntäminen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
cl::Program::Sources -olion luomisen kokonaan pois antamalla lähdekoodit suoraan merkkijonona cl::Program -luokan muodostinfunktiolle:cl::Program::Program(const Context& context,
const STRING_CLASS& source,
bool build,
cl_int * err = NULL)
build argumentin arvolla CL_TRUE, käännettäisiin ytimien lähdekoodit jo cl::Program -olion luonnin yhteydessä, jolloin meidän ei tarvitsisi kutsua build -jäsenfunktiota erikseencl::Kernel -luokan sisälle, jonka muodostinfunktio ottaa argumenttinaan Ohjelma-objektin ja ytimen nimen:cl::Kernel::Kernel(const Program& program,
const char * name,
cl_int * err = NULL)
cl::Kernel kernel(program, "add_one", &err);
if(err != CL_SUCCESS) {
std::cout << "Ytimen luominen epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}

template<typename T0,
typename T1 = detail::NullType,
...,
typename T31 = detail::NullType>
struct make_kernel::detail::functionImplementation<T0, T1, ..., T31>
cl::make_kernel::make_kernel(const Program &program,
const STRING_CLASS name,
cl_int *err = NULL)
auto kernel = cl::make_kernel<cl::Buffer&, int>(program, "add_one");
cl::CommandQueue-luokan sisälle, jonka muodostinfunktio ottaa argumenttinaan OpenCL-kontekstin ja OpenCL-laitteen, joihin komentojono on tarkoitus liittää:cl::CommandQueue::CommandQueue(
const Context& context,
const Device& device,
cl_command_queue_properties properties = 0,
cl_int * err = NULL)
cl::CommandQueue queue(context, oneDevice[0], 0, &err);
if(err != CL_SUCCESS) {
std::cout << "Komentojonon luominen epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}

cl::Buffer-luokan sisälle, joka ottaa argumenttinaan liittyvän OpenCL-kontekstin, flags-lippumuuttujan ja puskurin koon:cl::Buffer::Buffer(
const Context& context,
cl_mem_flags flags,
::size_t size,
void * host_ptr = NULL,
cl_int * err = NULL)
cl::Buffer deviceBuffer(context, CL_MEM_READ_WRITE, N*sizeof(int), 0, &err);
if(err != CL_SUCCESS) {
std::cout << "Muistin varaaminen epäonnistui epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}

enqueueWriteBuffer-jäsenfunktio asetaa komentojonoon käskyn kirjoittaa dataa isäntälaiteen muistista Puskuri-objektiinblocking_write-lipun asettaminen arvoon CL_TRUE tekee kutsusta blockaavan eli aliohjelmasta palataan vasta kun siirto on suoritettu loppuuncl_int cl::CommandQueue::enqueueWriteBuffer(
const Buffer& buffer,
cl_bool blocking_write,
::size_t offset,
::size_t size,
const void * ptr,
const VECTOR_CLASS<Event> * events = NULL,
Event * event = NULL)
err = queue.enqueueWriteBuffer(deviceBuffer, CL_FALSE, 0, N*sizeof(int),
hostBuffer, 0, 0);
if(err != CL_SUCCESS) {
std::cout << "Isäntälaite -> OpenCL-laite -siirtokäskyn asettaminen " \
"komentojonoon epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
}
setArg-jäsenfunktiolla:template <typename T>
cl_int cl::Kernel::setArg(cl_uint index, T value)
kernel.setArg(0, deviceBuffer);
if(err != CL_SUCCESS) {
std::cout << "Ytimen 1. argumentin asettaminen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
kernel.setArg(1, N);
if(err != CL_SUCCESS) {
std::cout << "Ytimen 2. argumentin asettaminen epäonnistui." <<
std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
enqueueNDRangeKernel-jäsenfunktiollaglobal-argumentilla ja säieryhmän koko vastaavasti local-argumentillacl_int cl::CommandQueue::enqueueNDRangeKernel(
const Kernel& kernel,
const NDRange& offset,
const NDRange& global,
const NDRange& local,
const VECTOR_CLASS<Event> * events = NULL,
Event * event = NULL)
::size_t maxWorkGroupSize;
oneDevice[0].getInfo(CL_DEVICE_MAX_WORK_GROUP_SIZE, &maxWorkGroupSize);
if(err != CL_SUCCESS) {
std::cout << "OpenCL-laitteen suurimman mahdollinen säieryhmän koon "\
"kysyminen epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
::size_t workGroupCount = N/maxWorkGroupSize+1;
cl::NDRange globalDim(workGroupCount*maxWorkGroupSize, 1, 1);
cl::NDRange localDim(maxWorkGroupSize, 1, 1);
err = queue.enqueueNDRangeKernel(kernel, cl::NullRange,
globalDim, localDim, 0, 0);
if(err != CL_SUCCESS) {
std::cout << "Ytimen käynnistyskäskyn asettaminen komentojonoon " \
"epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}
enqueueReadBuffer-jäsenfunktio asetaa komentojonoon käskyn lukea dataa Puskuri-objektista isäntälaiteen muistiinblocking_read-lipun asettaminen arvoon CL_TRUE tekee kutsusta blockaavan eli aliohjelmasta palataan vasta kun siirto on suoritettu loppuuncl_int cl::CommandQueue::enqueueReadBuffer(
const Buffer& buffer,
cl_bool blocking_read,
::size_t offset,
::size_t size,
const void * ptr,
const VECTOR_CLASS<Event> * events = NULL,
Event * event = NULL)
err = queue.enqueueReadBuffer(deviceBuffer, CL_TRUE, 0, N*sizeof(int),
hostBuffer, 0, 0);
if(err != CL_SUCCESS) {
std::cout << "OpenCL-laite -> Isäntälaite -siirtokäskyn asettaminen " \
"komentojonoon epäonnistui." << std::endl;
std::cerr << "OpenCL-virhekoodi: " << err << std::endl;
return 1;
}