CUDA Nedir
CUDA grafik motoruna gidecek veriyi hazırlamak için kullanılan bir hesaplama motoru gibi düşünülebilir. GPU'lar SIMD (Single Instruction Multiple Data) modeline göre tasarlanmışlardır. Veriyi küçük parçalara bölerek paralel çalıştırmaya uygundurlar. OpenCL'deki computing unit ile Cuda core farklı şeyler.
Program Yapısı
Kod device (GPU'da çalışır), host (CPU'da çalışır) ve global(CPU'da çalışır) ayrılıyor. Aşağıda host'un seri, device kodunun ise paralel çaşıltığı görülebilir.
Her device bloklara ayrılır. Her blok içinde ise thread'ler çalışır. Yani bloklar thread'leri gruplarlar.
Aşağıdaki şeklde bir bloğun içi görülebilir.
CUDA ile Dynamic Parallelism
GPU içindeki bir thread'in kendi kendine başka bir thread yaratabilmesi anlamına geliyor. Örnek:
Açıklaması ise şöyle:
Dynamic Parallelism on Kepler GPU dynamically spawns new threads by adapting to the data without going back to the CPU, greatly simplifying GPU programming and accelerating a broader set of popular algorithms.
Sürücü
Linux'ta Nvidia CUDA sürücüsünün sürümü şöyle öğrenilir.
nvdia-smi komutu
Şuna benzer bir çıktı verir.
Şuna benzer bir çıktı verir.
Derleyici
CUDA derleyicisi nvcc. CUDA kodları .cu uzantısı ile biter. Örneğin hello.cu.
Basit bir kodu
Şöyle yaparız.
Oluşmasını istediğimiz dosya ismidir.
Şu çıktıyı alırız.
cuda-memcheck
Varsa bellek hatasını gösterir.
Mantık şöyle.
1. Önce veri host bellekten device belleğe kopyalanır.
2. Hesaplama yapılır.
3. Veri bu sefer device bellekten, host belleğe kopyalanır.
metodlar
Metodları kullanbilmek için şu dosya include edilir.
__global__
GPU'da koşacak kodu belirtir. Örnekte her işlemci array'e 7 yazar. Bu örnekte blockIdx.x yukarıda bahsedilen blok numarasıdır. threadIdx.x ise blok içindeki thread numarasıdır.
cuda metodunu çağırırken <<<...>>> şeklinde kodlanır. Şöyle yaparız.
x,y,z şöyle hesaplanır. x ve y en fazla 1024 olabilir. z ise en fazla 64 olabilir.
__shared__
Tam nasıl kullanıldığını anlamadım ama şöyle tanımlanıyor.
Uygulama başlarken şöyle yaparız.
Şöyle yaparız.
İki farklı yöntem var.
1. Zero Copy
2. Device Copy
Zero Copy yöntemini anlamadım ancak sanırım daha hızlı çünkü device ve host bellekleri arasında kopyalama yapılmıyor.
Device Copy yönteminde device ve host bellekleri arasında kopyalama yapılıyor.
cudaHostAlloc metodu
Şöyle yaparız.
cudaHostAllıc ile host üzerinde yaratılan bellek alanının free edilmesi gerekir. Şöyle yaparız.
C'deki malloc'tan farklı olarak hata kodu döner. Şöyle kullanılabilir.
cudaMalloc ile device üzerinde yaratılan bellek alanının free edilmesi gerekir.
Eğer istenirse hata kotu string'e çevrilebilir. Şöyle yaparız.
Sanırım bir şekilde beraber kullanılıyorlar.
kernel<<<...,...>>> çağrısından sonra yapılır.
kernel<<<...>>> çağrısından sonra yapılır.
Event şöyle tanımlanır
Şöyle yaparız.
CUDA grafik motoruna gidecek veriyi hazırlamak için kullanılan bir hesaplama motoru gibi düşünülebilir. GPU'lar SIMD (Single Instruction Multiple Data) modeline göre tasarlanmışlardır. Veriyi küçük parçalara bölerek paralel çalıştırmaya uygundurlar. OpenCL'deki computing unit ile Cuda core farklı şeyler.
Program Yapısı
Kod device (GPU'da çalışır), host (CPU'da çalışır) ve global(CPU'da çalışır) ayrılıyor. Aşağıda host'un seri, device kodunun ise paralel çaşıltığı görülebilir.
Her device bloklara ayrılır. Her blok içinde ise thread'ler çalışır. Yani bloklar thread'leri gruplarlar.
Aşağıdaki şeklde bir bloğun içi görülebilir.
CUDA ile Dynamic Parallelism
GPU içindeki bir thread'in kendi kendine başka bir thread yaratabilmesi anlamına geliyor. Örnek:
Açıklaması ise şöyle:
Dynamic Parallelism on Kepler GPU dynamically spawns new threads by adapting to the data without going back to the CPU, greatly simplifying GPU programming and accelerating a broader set of popular algorithms.
Sürücü
Linux'ta Nvidia CUDA sürücüsünün sürümü şöyle öğrenilir.
$cat /proc/driver/nvidia/version
NVRM version: NVIDIA UNIX x86_64 Kernel Module 304.125 Mon Dec 1 19:58:28 PST 2014
GCC version: gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
Benim sistemimde Kernel Module 340.96 ve sene olarak 2015 yazıyor.nvdia-smi komutu
Şuna benzer bir çıktı verir.
+------------------------------------------------------+
| NVIDIA-SMI 352.79 Driver Version: 352.79 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GeForce GTX 780 Ti Off | 0000:01:00.0 N/A | N/A |
| 30% 43C P2 N/A / N/A | 877MiB / 3071MiB | N/A Default |
+-------------------------------+----------------------+----------------------+
| 1 GeForce GTX 780 Ti Off | 0000:03:00.0 N/A | N/A |
| 29% 25C P8 N/A / N/A | 11MiB / 3071MiB | N/A Default |
+-------------------------------+----------------------+----------------------+
deviceQuery komutuŞuna benzer bir çıktı verir.
Derleyici
CUDA derleyicisi nvcc. CUDA kodları .cu uzantısı ile biter. Örneğin hello.cu.
Basit bir kodu
#include <cstdlib>
#include <cstdio>
#include <cuda.h>
using namespace std;
__global__ void mykernel(void) {
}
int main(void) {
mykernel<<<1,1>>>();
printf("CPU Hello World!\n");
return 0;
}
Şöyle derleriz.$ nvcc hello.cu
$ a.out
Hello World!
--compiler-options seçeneğiŞöyle yaparız.
nvcc --ptxas-options=-v --compiler-options '-fPIC' --shared
lots of object files
lots of -L/wherever -lwhatever options
-o libOutput.so
-o seçeneğiOluşmasını istediğimiz dosya ismidir.
$ nvcc -o t1056 t1056.cu
-versionŞu çıktıyı alırız.
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2015 NVIDIA Corporation
Built on Tue_Aug_11_14:27:32_CDT_2015
Cuda compilation tools, release 7.5, V7.5.17
cuda-memcheck
Varsa bellek hatasını gösterir.
$ nvcc -o t1074 t1074.cu
$ cuda-memcheck ./t1074
========= CUDA-MEMCHECK
...
========= ERROR SUMMARY: 0 errors
Kodlama MantığıMantık şöyle.
1. Önce veri host bellekten device belleğe kopyalanır.
2. Hesaplama yapılır.
3. Veri bu sefer device bellekten, host belleğe kopyalanır.
metodlar
Metodları kullanbilmek için şu dosya include edilir.
#include<cuda.h>
__global__
GPU'da koşacak kodu belirtir. Örnekte her işlemci array'e 7 yazar. Bu örnekte blockIdx.x yukarıda bahsedilen blok numarasıdır. threadIdx.x ise blok içindeki thread numarasıdır.
__global__ void kernel(int *array)
{
int index = blockIdx.x * blockDim.x + threadIdx.x;
array[index] = 7;
}
Kod şöyle çağırılır.int main(void)
{
...
kernel<<<grid_size,block_size>>>(device_array);
...
}
blockIdx, blockDim, threadIdxcuda metodunu çağırırken <<<...>>> şeklinde kodlanır. Şöyle yaparız.
kernel<<<1, 267>>>(...);
İlk parametre kaç tane blok olacağını belirtir. İkinci parametre blok içinde kaç tane thread olacağını belirtir.
Sabit sayılar yerine dim3 değişkenleri de kullanılabilir.
Sabit sayılar yerine dim3 değişkenleri de kullanılabilir.
dim3 dimBlocks(numBlocks);
dim3 dimThreads(numThreadsPerBlock);
kernel<<<dimBlocks
, dimThreads>>>(...);
x,y,z şöyle hesaplanır. x ve y en fazla 1024 olabilir. z ise en fazla 64 olabilir.
__global__ void kernel(...)
{
unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
unsigned int z = blockIdx.z * blockDim.z + threadIdx.z;
...
}
__shared__
Tam nasıl kullanıldığını anlamadım ama şöyle tanımlanıyor.
extern __shared__ float myArray[];
cudaDeviceReset metoduUygulama başlarken şöyle yaparız.
int main(void) {
cudaDeviceReset();
cudaDeviceSynchronize();
...
}
cudaSetDeviceFlags metoduŞöyle yaparız.
cudaSetDeviceFlags(cudaDeviceMapHost);
Cuda Bellek Yönetimiİki farklı yöntem var.
1. Zero Copy
2. Device Copy
Zero Copy yöntemini anlamadım ancak sanırım daha hızlı çünkü device ve host bellekleri arasında kopyalama yapılmıyor.
Device Copy yönteminde device ve host bellekleri arasında kopyalama yapılıyor.
cudaHostAlloc metodu
Şöyle yaparız.
float *psrcMat;
cudaHostAlloc((void **)&psrcMat, 10*sizeof(float), cudaHostAllocMapped);
Bu bellek alanına şöyle yapılabilir.float *d_psrcMat;
//Map device to host pointers
cudaHostGetDevicePointer((void **)&d_psrcMat, (void *)psrcMat, 0);
cudaFreeHostcudaHostAllıc ile host üzerinde yaratılan bellek alanının free edilmesi gerekir. Şöyle yaparız.
cudaFreeHost(psrcMat);
cudaMalloc metoduC'deki malloc'tan farklı olarak hata kodu döner. Şöyle kullanılabilir.
void *fixed_cudaMalloc(size_t len)
{
void *p;
if (cudaMalloc(&p, len) == success_code)
return p;
return 0;
}
cduGetLastError ile beraber şöyle kullanılabilir.// Number of bytes in the matrix.
int bytes = 9 *sizeof(float);
// Pointers to the device arrays
float *matrixd=NULL;
// Allocate memory on the device to store matrix
cudaMalloc ((void**) &matrixd, bytes);
cudaError_t status = cudaGetLastError (); //Check the error
if (status != cudaSuccess) {
cudaFree (matrixd); //Free call for memory
...
}
cudaFree metoducudaMalloc ile device üzerinde yaratılan bellek alanının free edilmesi gerekir.
int main(void)
{
int num_bytes = 256;
// pointers to host & device arrays
int *device_array = 0;
// cudaMalloc a device array
cudaMalloc((void**)&device_array, num_bytes);
....
// deallocate memory
cudaFree(device_array);
}
cudaGetErrorString metoduEğer istenirse hata kotu string'e çevrilebilir. Şöyle yaparız.
cudaError_t code = ...;
if (code != cudaSuccess)
{
fprintf(stderr,"Error: %s\n", cudaGetErrorString(code));
...
}
cudaMemcpy metodu
Host bellekten device belleğe ya de device bellekten host bellğe kopyalamak için kullanılır. Şöyle yaparız.bool* deviceFlag;
cudaMalloc(&deviceFlag, sizeof(bool));
cudaMemcpy(deviceFlag, &hostFlag, sizeof(bool), cudaMemcpyHostToDevice);
Elimizde bir struct olsun ve bu struct'ı ile dolu bir de vector olsun.struct Atomic_Orbital{
L ag;
Cont cont;
};
vector<Atomic_Orbital> v;
v.resize(100);
for(int i=0; i<100; i++)
{
...
}
Bu vector'ü host bellekten device belleğe şöyle kopyalarızAtomic_Orbital *devPtr;
size_t size = B.size() * sizeof(Atomic_Orbital);
cudaMalloc(&devPtr, size);
cudaMemcpy(devPtr, B.data(), size, cudaMemcpyHostToDevice);
cudaMallocPitch ve cudaMemcpy2D metodlarıSanırım bir şekilde beraber kullanılıyorlar.
cudaMallocPitch(...);
cudaMemcpy2D(...);
cudaDeviceSyncronize metodukernel<<<...,...>>> çağrısından sonra yapılır.
kernel<<<...,...>>>(...);
cudaDeviceSynchronize();
cudaThreadSyncronize metodukernel<<<...>>> çağrısından sonra yapılır.
kernel<<<...,...>>>(...);
cudaThreadSynchronize();
cudaEvent_tEvent şöyle tanımlanır
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
Daha sonra şöyle yaparız.cudaEventRecord(start,0);
...//Call something
cudaEventRecord(stop,0);
cudaEventSynchronize(stop);
float milliseconds = 0;
cudaEventElapsedTime(&milliseconds, start, stop);
cudaStream_tŞöyle yaparız.
cudaStream_t stream1;
cudaStreamCreate(&stream1);
thrust
thrust yazısına taşıdım.
Hiç yorum yok:
Yorum Gönder