CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE
-lippu, jonka avulla ytimeltä voidaan kysyä luku, jonka moninkerrat soveltuisivat säieryhmän kooksi (linkki)cudaOccupancyMaxPotentialBlockSize()
ja cudaOccupancyMaxPotentialBlockSizeVariableSMem()
aliohjelmat, jotka tarjoavat heuristiikkoja säieryhmän koon valintaan (linkki)#define CEIL(x,y) (((x)+(y)-1)/(y)) // x/y pyöristettynä ylöspäin
#define ROUND_TO_NEXT(x,n) ((n)*CEIL(x,n)) // Pyöristää seuraavaan moninkertaan
#define WARPFRONT 32 // Nvidian näytönohjain
cl_uint height= 125:
cl_uint width = 432;
// Lasketaan seuraava 32 moninkerta eli tässä tapauksessa 448
cl_uint ldf = ROUND_TO_NEXT(width,WARPFRONT);
// Varataan height x ldf int-taulukko
cl::Buffer deviceBuffer(context, CL_MEM_READ_WRITE, height*ldf*sizeof(int));
// Asetetaan ydin komentojonoon
kernel(cl::EnqueueArgs(...), deviceBuffer, height, width, ldf);
ldf
(rivin pituus alkioina) välitetään ytimelle, joka hyödyntää sitä taulukon käsittelyssä:__kernel void kernel(__global int *buffer, uint height, uint width, uint ldf) {
...
int value = buffer[i*ldf+j]; // Sama kuin buffer[i][j]
...
}
cl::Device::getInfo
-jäsenfunktiolla, jolloin taulukon rivin pituudeksi voidaan asettaa välimuistin rivin pituuden moninkerta:#define CEIL(x,y) (((x)+(y)-1)/(y)) // x/y pyöristettynä ylöspäin
#define ROUND_TO_NEXT(x,n) ((n)*CEIL(x,n)) // Pyöristää seuraavaan moninkertaan
cl_uint cacheLine;
device.getInfo(CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, &cacheLine);
if(cacheLine < 1)
cacheLine = 1;
cl_uint height = 125:
cl_uint width = 432;
// Lasketaan seuraava välimuistin rivin pituuden moninkerta
cl_uint ldf = ROUND_TO_NEXT(width*sizeof(int), cacheLine);
// Varataan taulukko, jossa jokaisen rivin pituus on ldf tavua
cl::Buffer deviceBuffer(context, CL_MEM_READ_WRITE, height*ldf);
kernel(cl::EnqueueArgs(...), deviceBuffer, height, width, ldf);
ldf
välittää nyt tiedon rivin pituudesta tavuina. Huomaa myös, että buffer
-taulukko välitetään char
-tyyppisenä.__kernel void kernel(__global char *buffer, uint height, uint width, uint ldf) {
...
// Lasketaan rivi; buffer[i][*]
__global int *line = (__global int*)(buffer + i*ldf);
int value1 = line[j]; // Sama kuin buffer[i][j]
int value2 = *((int*)(buffer + i*ldf) + j); // Sama kuin buffer[i][j]
...
}
cudaMallocPitch
-aliohjelman kaksiulotteisien taulukoiden varaamiseen:cudaError_t cudaMallocPitch(
void **devPtr, // Osoitin taulukon alkuun
size_t *ldf, // Optimaalinen arvo taulukon rivin pituudeksi tavuina
size_t height, // Taulukon korkeus alkioina
size_t width // Taulukon leveys _tavuina_
);
size_t height = 125:
size_t width = 432;
size_t ldf;
char* deviceBuffer;
// Varataan taulukko, jossa jokaisen rivin pituus on ldf tavua
cudaMallocPitch(&deviceBuffer, &ldf, width * sizeof(int), height);
kernel<<<...>>>(deviceBuffer, height, width, ldf);
cudaMemcpy2DAsync
-aliohjelmalla:int *hostBuffer;
cudaMallocHost(&hostBuffer, height * width * sizeof(int));
// hostBuffer täytetään datalla tässä välillä...
err = cudaMemcpy2DAsync(
deviceBuffer, // Kohde
ldf, // Riviä varten varattu tila tavuina kohteessa
hostBuffer, // Lähde
width * sizeof(int), // Riviä varten varattu tila tavuina lähteessä
width * sizeof(int), // Taulukon leveys tavuina
height, // Taulukon korkeus
cudaMemcpyHostToDevice, // Siirron suunta
0); // Komentojono
ldf
välittää tiedon rivin pituudesta tavuina:__global__ void kernel(char *buffer, size_t height, size_t width, size_t ldf) {
...
int *line = (int*)(buffer + i*ldf); // Lasketaan rivin
int value1 = line[j]; // Sama kuin buffer[i][j]
int value2 = *((int*)(buffer + i*ldf) + j); // Sama kuin buffer[i][j]
...
}
cudaMalloc3D
-aliohjelman kolmiulotteisien taulukoiden varaamiseen:cudaError_t cudaMalloc3D(
struct cudaPitchedPtr *pitchedDevPtr, // Varattu 3-ulotteinen taulukko
struct cudaExtent extent // Tieto taulukon koosta
);
struct cudaExtent make_cudaExtent(
size_t w, // Taulukon leveys tavuina
size_t h, // Taulukon korkeus alkioina
size_t d); // Taulukon syvyys alkioina
struct cudaPitchedPtr {
size_t pitch; // Yhdelle taulukon riville varattu tila tavuina
void * ptr; // Osoitin taulukon alkuun
size_t xsize;
size_t ysize;
};
struct cudaExtent {
size_t depth;
size_t height;
size_t width;
};
size_t width = 54, height = 23, depth = 5;
// Luodaan cudaExtent structi 3D-taulukon varaamista varten
cudaExtent extent = make_cudaExtent(width * sizeof(int), height, depth);
// Varataan 3D-taulukko edellä luodun cudaExtentin perusteella
cudaPitchedPtr pitchPointer;
cudaMalloc3D(&pitchPointer, extent);
// Kutsutaan ydintä
kernel<<<...>>>(pitchPointer, width, height, depth);
__global__ void kernel(
cudaPitchedPtr pitchPointer, size_t width, size_t height, size_t depth) {
...
// Lasketaan rivin (i,j,*) alku
int *line = (int*)(
(char*)pitchPointer.ptr + (i*height + j)*pitchPointer.pitch);
int value = line[k]; // Sama kuin buffer[i][j][k]
}
__kernel void (__global float *A, uint height, uint width) {
const int i = get_group_id(1); // height säieryhmää suuntaan 1
const int j = get_global_id(0); // Vähintään width säiettä suuntaan 0
float value = 0.0;
if(j < width)
value = A[i][j]; // Jokainen säie lukee yhden luvun taulukosta
...
}
int *buffer;
cudaMalloc((void **)&buffer, N*sizeof(int));
// Alipuskuri, joka alkaa indeksista N/3
int *subBuffer = buffer + N/3;
kernel<<<...>>>(buffer, ...); // Suorittaa operaation koko buffer-taulukolle
kernel<<<...>>>(subBuffer, ...); // Suorittaa operaation pelkästään osalle
// buff-taulukkoa
cl::Buffer
-luokan createSubBuffer
-jäsenfunktiolla:cl::Buffer cl::Buffer::createSubBuffer(
cl_mem_flags flags,
cl_buffer_create_type buffer_create_type,
const void *buffer_create_info,
cl_int *err = NULL)
cl::Buffer buffer(context, CL_MEM_READ_WRITE, N*sizeof(int));
cl_buffer_region region;
region.origin = N/3; // Alipuskuri alkaa indeksistä N/3
region.size = N/3; // Alipuskurin koko on N/3
cl::Buffer subBuffer = buffer.createSubBuffer(
CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion);
cudaMallocHost
ja cudaFreeHost
aliohjelmilla:cudaError_t cudaMallocHost(void **ptr, size_t size);
cudaError_t cudaFreeHost(void *ptr);
CL_MEM_ALLOC_HOST_PTR
annettu cl::Buffer
-olion luonnin yhteydessäCL_MEM_USE_HOST_PTR
annettu cl::Buffer
-olion luonnin yhteydessäcl::Buffer
-olio luotu ilman ylimääräisiä lippujaCL_MEM_USE_PERSISTENT_MEM_AMD
-lipullaanCL_MEM_ALLOC_HOST_PTR
-lipun antaminen cl::Buffer
-luokan muodostinfunktiolle kertoo OpenCL-toteutukselle, että OpenCL-toteutuksen tuli varata muisti siten, että isäntälaitteella on pääsy siihen:cl::Buffer deviceBuffer = cl::Buffer(context,
CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, N*sizeof(int));
enqueueMapBuffer
-jäsenfunktiolla:void * cl::CommandQueue::enqueueMapBuffer(
const Buffer& buffer,
cl_bool blocking_map,
cl_map_flags flags,
::size_t offset,
::size_t size,
const VECTOR_CLASS<Event> *events = NULL,
Event *event = NULL,
cl_int *err = NULL);
blocking_map
-lipun asettaminen arvoon CL_TRUE
tekee mappaamiseoperaatiosta blockkaavanflags
-lippu määrittää onko muistipuskuri tarkoitus mapata luettavaksi (CL_MAP_READ
) vai kirjoitettavaksi (CL_MAP_WRITE
)deviceBuffer
osaksi isäntälaitteen muistia blockkaavalla operaatiolla siten, että CPU voi kirjoittaa mapattuun muistialueeseen:int *hostPointer = 0;
hostPointer = (int*) queue.enqueueMapBuffer(
deviceBuffer, CL_TRUE, CL_MAP_WRITE, 0, N*sizeof(int));
hostPointer
)for(int i = 0; i < N; i++)
hostPointer[i] = checkBuffer[i];
enqueueUnmapMemObject
-jäsenfunktiolla:cl_int cl::CommandQueue::enqueueUnmapMemObject(
const Memory &memory,
void *mapped_ptr,
const VECTOR_CLASS<Event> *events = NULL,
Event *event = NULL)
deviceBuffer
mapattiin isäntäohjelman muistiavaruudessa sijaitsevaan muistialueseen (hostPointer
):queue.enqueueUnmapMemObject(deviceBuffer, hostPointer);
kernel(cl::EnqueueArgs(...), deviceBuffer, ...);
deviceBuffer
osaksi isäntäohjelman muistiavaruutta siten, että isäntäohjelma voi lukea mapatusta muistialueesta:hostPointer = (int*) queue.enqueueMapBuffer(
deviceBuffer, CL_TRUE, CL_MAP_READ, 0, N*sizeof(int));
int rightValues = 0;
for(int i = 0; i < N; i++)
if(hostPointer[i] == checkBuffer[i]+1)
rightValues++;
queue.enqueueUnmapMemObject(deviceBuffer, hostBuffer);
int *hostBuffer = new int[N];
hostBuffer
täytyy olla varattu siten, että muistialueen alun osoite on OpenCL-laitteen puolella käytettävän tietotyypin moninkerta!new
-operaattorin palauttama osoitin on usein valmiiksi sopiva perustietotyyppien tapauksessa. Muissa tapauksissa joudutaan ehkä soveltamaan hieman osoitinaritmetiikkaa.cl::Buffer
-olion luomisen yhteydessä:cl::Buffer deviceBuffer = cl::Buffer(context,
CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, N*sizeof(int), hostBuffer);
CL_MEM_USE_HOST_PTR
-lippu kertoo, että OpenCL-toteutus voi käyttää hostBuffer
-osoittimen viittaamaa muistialuetta vapaasti mappausoperatioissa. Puskuria ei voi käyttää enää muihin tarkoituksiin!enqueueMapBuffer
-aliohjelma palauttaa käytännössä cl::Buffer
-olion luonnin yhteydessä annetun osoitteen. OpenCL-määritykset eivät kuitenkaan takaa tätä, joten mapatun muistialueen osoite kannattaa tallentaa aina erilliseen osoitinmuuttujaan:int *hostPointer = 0;
hostPointer = (int*) queue.enqueueMapBuffer(
deviceBuffer, CL_TRUE, CL_MAP_WRITE, 0, N*sizeof(int));
cl::Buffer
-olio voidaan luoda myöskin ilman CL_MEM_ALLOC_HOST_PTR
ja CL_MEM_USE_HOST_PTR
lippujaMuistin mappaaminen vaatii CPU:n ja GPU:n suorituksen synkronointia
Seuraavassa esimerkissä synkronointi on toteutettu komentojonon avulla:
cudaStream_t queue;
cudaStreamCreate(&queue);
cudaDeviceSynchronize()
-aliohjelmallaint *hostPointer;
cudaMallocHost(&hostPointer, N*sizeof(int), cudaHostAllocMapped);
cudaHostAllocMapped
-lippu kertoo CUDA:lle, että varattu muistialue näkyy CUDA-laitteelle
Isäntäohjelma voi kirjoittaa laskentaan käytettävän datan varattuun muistialueeseen normaalisti:
for(int i = 0; i < N; i++)
hostPointer[i] = (i & 0x1) * i;
int *devicePointer;
cudaHostGetDevicePointer(&devicePointer, hostPointer, 0);
devicePointer
-osoitinta:kernel<<<..., queue>>>(devicePointer, ...);
hostPointer
-osoitinta:kernel<<<..., queue>>>(hostPointer, ...);
cudaStreamSynchronize(queue);
Muussa tapauksessa hostPointer
ei olisi vielä välttämättä valmis käytettäväksi
Synkronoinnin jälkeen isäntäohjelma voi käyttää hostPointer
-osoitinta normaalisti:
int rightValues = 0;
for(int i = 0; i < N; i++)
if(hostPointer[i] == (i & 0x1) * i + 1)
rightValues++;