OpenCL

OpenCL, (Open Computing Language), Apple tarafından 2008 yılında kar amacı gütmeyen teknoloji şirketleri birliği Khronos Group'a önerilen, kabul gördükten sonra spesifikasyonu pek çok şirketin katkılarıyla hazılanan heterojen hesaplama platformudur. OpenCL; destekli grafik işlemcileri, genel amaçlı işlemciler ve FPGA ler gibi farklı platformlarda hesaplama yapılmasına olanak sağlar. OpenCL AMD, Intel, NVIDIA ve ARM tarafından desteklenmektedir. Ayrıca OpenCL kullanılarak Sony Playstation cihazlarında kullanılan Cell işlemcilerde de hesaplama yapılabilmektedir.
OpenCL C Dili
OpenCL destekli cihazlarda(bundan sonra cihaz olarak geçecektir) çalıştırılacak kodun yazılması için OpenCL C de denilen, C programlama dilinin ISO/IEC 9899:1999 sürümünü baz alan bir programlama dili geliştirilmiştir. OpenCL C dilinde paralelliğin daha iyi kullanılabilmesine olanak sağlamak için C programlama dilinden bazı farklılıklara gidilmiştir. Bu farklılıklardan en çok göze çarpanları:
Fonksiyon işaretcileri(function pointers) kaldırılmıştır.
Özyineleme(recursion) kaldırılmıştır.
Değişken uzunluklu diziler kaldırılmıştır.
Vektör veri türleri eklenmiştir
Senkronizasyon ve çalışma modeline ilişkin pek çok fonksiyon eklenmiştir.
Hafıza yönetimi için __global, __local, __constant ve __private sıfatları eklenmiştir.
OpenCL Çalışma Mantığı
Kernel
Kernel cihaz üzerinde çalıştırılabilen en temel kod dizisine verilen isimdir. Kerneller OpenCL'in ilk versiyonlarında C dilinde yazılıyordu. Artık C++ dilinin güçlü imkanları da kullanılmaktadır. C++14 versiyonu kullanılmaktadır. Kernelları cihaz üzerinde çalışacak C/C++ fonksiyonları gibi düşünebiliriz. Kerneller veri-paralel veya iş-paralel olabilirler, fakat önemli nokta paralel olmalarıdır.
İş-Parçaları, İş-Öbekleri(Work-item, Work-group)
İş-parçaları OpenCL paralel çalıştırma hiyerarşisinde en küçük parçalardır. Bir kernel her bir iş parçası için çalıştırılır. Her bir iş-parçasının kendi hafızası bulunur ve bu hafıza __private sıfatıyla ayrılır. İş-parçaları gruplanarak iş-öbeklerini oluştururlar, aynı iş-öbeğindeki tüm iş-parçalarının paralelde birlikte çalıştırılacağı garanti altına alınmıştır. Her iş-öbeğinin, iş-parçaları arasında paylaşılan bir hafızası vardır ve bu hafıza __local sıfatıyla ayrılır. Farklı iş-öbekleri birbirlerinin __local hafızalarına erişemezler. Çalıştırılacak iş-parçası sayısına genel iş büyüklüğü(global work size) denilir, her bir öbekteki iş parçacığı sayısı ise yerel iş büyüklüğü(local work size) ile belirlenir. Tahmin edilebileceği gibi genel iş büyüklüğü, yerel iş büyüklüğünün tam katı olmalıdır. __global ve __constant hafıza alanları ise tüm iş-parçaları tarafından erişilebilen hafıza alanlarıdır.
OpenCL Paralel İşleme Mantığı
OpenCL çalışma prensibi paralelliği daha etkinleştirebilmek için boyutlu bir yapıdadır. Tek boyutlu bir diziyi işlemek için tek boyutlu bir iş-parçacığı mantığı kullanabiliriz, fakat resim veya 3 boyutlu texture gibi çok boyutlu bir yapıyı işlemek istiyorsak çok boyutlu bir iş-parçacığı mantığı kullanmalıyız. Örneğin 640 480 büyüklüğündeki 5 resmi tek bir kernel üzerinden pixel bazlı olarak işlemek istersek, 1. çalışma boyutunu 640, 2. çalışma boyutunu 480 ve 3. çalışma boyutunu 5 olarak atayabiliriz, bu durumda 640 480 5 = 1536000 adet iş-parçası çalıştırılacaktır, eğer yerel iş büyüklüğünü 64 olarak seçersek, 1536000 / 64 = 24000 adet iş-öbeği çalıştırılacaktır.
OpenCL Çalışma Dizi Toplama Örneği
Standart C dilinde 2 diziyi toplama işlemini aşağıdaki gibi kod ile gerçekleştirebiliriz
void diziTopla(
int n,
const float a,
const float b,
float result
){
int i;
for(i = 0 ; i < n ; i++)
result[i] = a[i] + b[i];
}
Bu işlemi OpenCL ile yapmak için ise aşağıdaki gibi paralel çalışacak bir kernel yazabiliriz, bu kernel her iş-parçası için 1 kere çalıştırılacak, dolayısıyla iş parçası indisi ile o kernel da işlenecek dizi elemanlarına ulaşıyoruz. Bu durumda tek boyutlu bir iş büyüklüğü kullanabiliriz, genel iş büyüklüğünü dizinin uzunluğuna ayarlarsak her bir dizi elemanı için bir iş parçası çalıştırılmasını sağlayabiliriz.
__kernel void diziTopla(
__global const float a,
__global const float b,
__global float result
){
int id = get_global_id(0); // iş parçası indisini alıyor
result[id] = a[id] + b[id];
}