OpenCLの抂芁

この蚘事では、OpenClプログラミングの基本に぀いお説明したす。 OpenClは、 c99暙準に構造が䌌おいるGPU / CPUプログラミング蚀語です。 Khronos Groupは開発に取り組んでおり、完党なドキュメントはWebサむトで入手できたす。 「たあ、それはささいなこずで、むンタヌネットを掘り起こすだけ」ずいうトピックに関する論争を避けるために、私はすぐに予玄をしたす。 ここでは、初心者プログラマの生掻を可胜な限り簡玠化し、最初のプロゞェクトのビデオカヌドの蚈算胜力を䜿甚できるようにする基本原則をたずめたす。 2〜3個の深刻なOpenClプログラムを曞いた人は、もう興味を瀺さなくなりたす。 この蚘事はある意味で私の最埌の蚘事の続きです。



コンパむラ



たず第䞀に、問題は、コヌド自䜓をどこに曞くかです。 私が知る限り、.NETには、スタゞオでカヌネルコヌドを盎接凊理するためのwhiはありたせん。 したがっお、サヌドパヌティの゚ディタヌを䜿甚する必芁がありたす。 AMD、nVidia、およびIntelは、それらのSDKをバンドルしおいたす。 䜕らかの理由で、Intelovskyの方が奜きです。 オプションずしお、ファンによっお曞かれたいく぀かの゚ディタヌがありたす。 これらのうち、私はOpenCLTemplateに最も付属しおいる゚ディタヌが奜きです。 これらが線集者であるこずは泚目に倀したす。 コヌドのコンパむルは、GPU / CPUで実行する盎前に発生したす。



デバむスメモリモデル



画像

蚀語自䜓を説明する前に、察話するデバむスの物理モデルに぀いお簡単に説明したす。 蚀語コマンドは、「ワヌクアむテム」ず呌ばれるオブゞェクトで実行されたす。 各「䜜業項目」は互いに独立しおおり、残りず䞊行しおコヌドを実行できたす。 プロセスが、䜿甚䞭のワヌクアむテムたたは他のワヌクアむテムによっおすでに凊理されたワヌクアむテムからデヌタを受信する堎合、共有メモリを介しおこれを実行できたす。 合蚈メモリは非垞に遅くなりたすが、倧量にありたす。 蚈算を高速化するために、ロヌカルメモリがありたす。 CUDAに粟通しおいる堎合、「共有メモリ」ず呌ばれたす。 䞀般的な方法よりもはるかに高速ですが、すべおのプロセスがアクセスできるわけではありたせん。 1぀のグルヌプの䜜業項目のみがロヌカルメモリにアクセスできたす。 これらのグルヌプは、「コンピュヌティングナニット」たたは「ワヌクグルヌプ」ず呌ばれたす最初の名前は鉄レベルの物理パヌティションを指し、2番目はプログラムレベルの論理パヌティションを指したす。 デバむスに応じお、これらの各グルヌプには異なる数の䜜業項目がありたすたずえば、NVIDIA GT200では240、Radeon 5700シリヌズでは256。 これらのナニットの数はかなり少ない数に制限されおいたすNVIDIA GT200では30、Radeon 5700シリヌズでは9-10。 ワヌクアむテムが単独でアクセスできる超高速の「プラむベヌトメモリ」もありたす。

OpenCLデバむスドラむバヌは、ワヌクアむテムずワヌクグルヌプの起動ず操䜜を自動化したす。 たずえば、100䞇個のプロセスを実行する必芁があり、自由に䜿えるワヌクアむテムが1,000個しかない堎合、ドラむバヌは完了埌に次のタスクで各プロセスを自動的に開始したす。 物理レベルの理解は、プロセス間の盞互䜜甚ずメモリぞのプロセスのアクセスの可胜性を理解するためにのみ必芁です。



基本機胜



この蚀語はほが暙準のC ++に基づいおいるため、OpenCLずそれを区別する機胜のみを怜蚎したす。 前回の蚘事で匕甚した最も単玔なカヌネルプログラムのコヌドを怜蚎しおください。 このコヌドは、v1ずv2の2぀のベクトルを远加し、結果を最初のベクトルに入れたす。

__kernel void

floatVectorSum(__global float * v1,

__global float * v2)

{

int i = get_global_id(0);

v1[i] = v1[i] + v2[i];

}



* This source code was highlighted with Source Code Highlighter .








手続きのお知らせ


たず、神秘的な「__kernel」が目を匕きたす。 このディレクティブは、倖郚から呌び出すプロシヌゞャをマヌクする必芁がありたす。 倖郚から䜜業するずきに手順が䞍芁な堎合は、省略できたす。



メモリの皮類


デヌタ型「__global」は、実行䞭のデバむスのグロヌバルアドレス空間から割り圓おられたメモリを指したす。 かなり遅いですが、ゆずりがありたす。 最新のビデオカヌドの堎合、ギガバむト単䜍で枬定されたす。 プロセッサで䜜業する堎合、グロヌバルはRAMを指したす。

グロヌバルに加えお、「__ local」がありたす。 ワヌクグルヌプのみがアクセスできたす。 そのようなグルヌプごずに、玄8キロバむトが割り圓おられたす。

たた、高速メモリは「__privat」です。 これは、別のスレッドワヌクアむテムのみがアクセスできるメモリです。 合蚈で、このメモリの32個のレゞスタがストリヌムに割り圓おられたす。

カヌネルの䜜成時に宣蚀できる残りのタむプのメモリは、__ globalタむプに基づいおいたす。 1぀は「__constant」で、読み取り専甚で䜿甚できたす。 次に、これらは「__read_only」、「__ write_only」、および「__read_write」です。これらの構造の䜿甚は画像に察しおのみ蚱可されおいたす。



プロセス識別子


ビデオカヌドで起動した埌、すべおのプロセスは同等であり、同等のコヌドを実行したす。 しかし、明らかに、同じアクションを耇数回繰り返す必芁はありたせん。各プロセスはタスクの独自の郚分を実行する必芁がありたす。 それらを取り巻く䞖界で圌らの堎所を特定するこずは、プロセス識別子です。 最も単玔な識別子は「get_global_id0」です。 䞊蚘の䟋の堎合、このプロセスが远加するベクトルのi番号を指したす。 1次元のベクトルではなく2次元の画像を凊理する堎合、2぀の軞に沿ったプロセスの䜍眮を知る必芁がありたす。 もちろん、この倀は蚈算できたす。 しかし、これらは䞍必芁な操䜜です。 そのため、䟿宜䞊、起動時に2次元空間が必芁であるこずを指定できたす。 その埌、プロセスで䞡方の䜍眮識別子を取埗できたす「get_global_id0」、「get_global_id1」。 3次元空間でも同じこずができたす。 倚くの堎合、䜜業するスペヌスの寞法も必芁になる堎合がありたす。 たずえば、ほずんどすべおの凊理䞭の画像の堎合、幅ず高さが必芁です。 スペヌスの次元を取埗するには、識別子「get_global_sizei」が䜿甚されたす。 さらに、ワヌキンググルヌプ内のプロセス識別子-「get_local_idi」、「get_local_sizei」、およびグルヌプ自䜓の識別子-「get_group_idi」、「get_num_groupsi」がありたす。 これらの関係のほずんどは互いに関連しおいたすnum_groups * local_size = global_size、local_id + group_id * local_size = global_id、global_sizelocal_size = 0



課金の最適化



OpenCLおよびビデオカヌドの開発者は、圌らの発案の䞻な目暙は耇雑な蚈算を加速するこずであるこずに気付きたした。 これを行うために、いく぀かの特殊な機胜が蚀語に远加されたした。これらの機胜を䜿甚するず、数孊的な問題を迅速に凊理できたす。



むンラむンベクトル


最初の機胜は、ベクトルずベクトル挔算です。 OpenClでは、2、4、8、および16次元のベクトルを倉数ずしお宣蚀できたす。 これはそれに応じお行われたすint2、int4、int8、int16。 たた、double、byte、および他のすべおのタむプを宣蚀できたす。 察応する次元のベクトルは、加算/枛算/陀算/乗算するこずができたす。同様に、任意のベクトルを数倀で陀算/乗算するこずができたす。

uint4 sumall = (uint4)(1,1,1,1);

small += (uint4)(1,1,1,1);

sumall = sumall/2;



* This source code was highlighted with Source Code Highlighter .






さらに、ベクタヌ甚に最適化された倚数の関数があり、それらを盎接操䜜できたす。 このような関数には、距離蚈算関数、ベクトル積関数が含たれたす。 䟋

float4 dir1 = (float4)(1, 1, 1, 0);

float4 dir2 = (float4)(1, 2, 3, 0);

float4 normal = cross(dir1, dir2);



* This source code was highlighted with Source Code Highlighter .






たた、ベクトルをマヌゞしお、䞀方から他方に郚品を取り、それらをより倧きなものに接着するこずもできたす。

int4 vi0 = (int4) -7 ;

int4 vi1 = (int4) ( 0, 1, 2, 3 ) ;

vi0.lo = vi1.hi; //

int8 v8 = (int8)(vi0.s0123, vi1.s0123); //



* This source code was highlighted with Source Code Highlighter .








シンプルな機胜


OpenClのもう1぀の機胜は、組み蟌み関数ラむブラリです。 OpenClのmath.libの暙準セットに加えお、いわゆるネむティブ関数がありたす。 これらは、ビデオカヌドの特定の機胜の䜿甚ず倱瀌な数孊に盎接基づく機胜です。 それらを超粟密な蚈算で䜿甚するこずはお勧めできたせんが、画像フィルタリングの堎合、違いに気付くこずはできたせん。 このような関数には、たずえば、「native_sin」、「native_cos」、「native_powr」が含たれたす。 これらの機胜の詳现な説明は行いたせんが、倚くの機胜があり、原則は異なりたす。 必芁な堎合は、ドキュメントを参照しおください。



䞀般的な機胜


「単玔な関数」に加えお、開発者は倚くの呌ばれる共通関数を䜜成したした。 これらは、画像凊理で䞀般的な機胜です。 䟋mada、b、c= a * b + c、mixa、b、c= a +ba* c。 これらの関数は、察応する数孊アクションよりも高速です。



䟋


サむトwww.cmsoft.com.brには、ネむティブ関数ず共通関数を䜿甚しおコヌドを最適化する可胜性を瀺す玠晎らしい䟋がありたす。

kernel void regularFuncs()

{

for ( int i=0; i<5000; i++)

{

float a=1, b=2, c=3, d=4;

float e = a*b+c;

e = a*b+c*d;

e = sin(a);

e = cos(b);

e = a*b+c*d;

e = sin(a);

e = cos(b);

e = a*b+c*d;

e = sin(a);

e = cos(b);

float4 vec1 = (float4)(1, 2, 3, 0);

float4 vec2 = (float4)(-1, 3, 1, 0);

float4 vec = distance(vec1, vec2);

double x=1, y=2, z=3;

double resp = x*y+z;

}

}

kernel void nativeFuncs()

{

for ( int i=0; i<5000; i++)

{

float a=1, b=2, c=3, d=4;

float e = mad(a,b,c);

e = mad(a,b,c*d);

e = native_sin(a);

e = native_cos(b);

e = mad(a,b,c*d);

e = native_sin(a);

e = native_cos(b);

e = mad(a,b,c*d);

e = native_sin(a);

e = native_cos(b);

float4 vec1 = (float4)(1, 2, 3, 0);

float4 vec2 = (float4)(-1, 3, 1, 0);

float4 vec = fast_distance(vec1, vec2);

double x=1, y=2, z=3;

double resp = mad(x,y,z);

}

}



* This source code was highlighted with Source Code Highlighter .






2番目の手順最適化を䜿甚は35倍高速です。



蚱可



OpenClには、さたざたな远加機胜を含めるこずができる倚くのディレクティブがあるこずに泚意しおください。 これには2぀の理由がありたす。 最初-歎史的に、これらの機胜のすべおがサポヌトされおいたせんでした。 第二に、これらの機胜はパフォヌマンスに圱響を䞎える可胜性がありたす。 通垞、機胜は次のコマンドによっお有効になりたす。

#pragma OPENCL EXTENSION extension name : behavior



* This source code was highlighted with Source Code Highlighter .






䟋ずしお。 次のコマンドが含たれたすバむトタむプ、蚈算の倍粟床、およびすべおの数孊関数を䜿甚する機胜

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable

#pragma OPENCL EXTENSION cl_khr_fp64 : enable



* This source code was highlighted with Source Code Highlighter .








同期する





障壁


倚くの堎合、コンピュヌティングでは、同期が必芁です。 これはいく぀かの方法で実珟されたす。 最初は障壁です。 バリアは、他のすべおのプロセスたたはそのワヌキンググルヌプのプロセスに到達するたでプロセスが停止するようなチヌムです。 2぀の䟋を瀺したす。

kernel void localVarExample()

{

int i = get_global_id(0);

__local int x[10];

x[i] = i;

barrier(CLK_LOCAL_MEM_FENCE);

if (i>0) int y = x[i-1];

}

kernel void globalVarExample()

{

int i = get_global_id(0);

__global int x[10];

x[i] = i;

barrier(CLK_GLOBAL_MEM_FENCE);

if (i>0) int y = x[i-1];

}



* This source code was highlighted with Source Code Highlighter .








最初の䟋では、バリアコマンドでワヌクグルヌプのすべおのプロセスが予想され、2番目では、OpenCLデバむスのすべおのプロセスが予想されたす。

この䟋の特城であるコマンド「__local int x [10];」に泚目する䟡倀がありたす。 および「__global int x [10];」。 これらを䜿甚するず、プロセスのグルヌプおよび実行䞭のすべおのプロセスでグロヌバル倉数を遞択できたす。



単䞀の操䜜


スレッド間で同期するための2番目のオプションはアトミックです。 これらは、メモリぞの同時アクセスを防ぐ機胜です。 それらを䜿甚する前に、次のディレクティブを含める必芁がありたす。

#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable

#pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable

#pragma OPENCL EXTENSION cl_khr_global_int32_extended_atomics : enable

#pragma OPENCL EXTENSION cl_khr_local_int32_extended_atomics : enable

#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable

#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable



* This source code was highlighted with Source Code Highlighter .






この関数の最も簡単な䟋

__kernel void test(global int * num)

{

atom_inc(&num[0]);

}



* This source code was highlighted with Source Code Highlighter .






「atom_incnum [0];」の代わりにnum ++が曞き蟌たれた堎合、すべおのプロセスが同時にメモリにアクセスしお同じ倀を読み取るため、プログラム実行の結果は予枬䞍胜でした。 合蚈で、11のナニット操䜜関数がありたす「add、sub、xchg、inc、dec、cmp_xchg、min、max、および、or、xor」。

これらの関数を䜿甚しおセマフォを䜜成するこずは難しくありたせん。

void GetSemaphor(__global int * semaphor) {

int occupied = atom_xchg(semaphor, 1);

while (occupied > 0)

{

occupied = atom_xchg(semaphor, 1);

}

}



void ReleaseSemaphor(__global int * semaphor)

{

int prevVal = atom_xchg(semaphor, 0);

}



* This source code was highlighted with Source Code Highlighter .








画像を操䜜する





このガむドに最埌に含めたいのは、OpenCLを介しお画像を操䜜するこずです。 クリ゚ヌタヌは、ナヌザヌの脳を最小限に抑える必芁がある画像を䜿甚しお䜜品を䜜成しようずしたした。 ずおもいいです。 タむプimage2d_tおよびimage3d_tの画像のダりンロヌドが可胜です。 前者は普通の画像で、埌者は䞉次元です。 たた、ロヌドされるむメヌゞは、「__ read_only」、「__ write_only」、「__ read_write」のいずれかの圢匏である必芁がありたす。 むメヌゞからのデヌタの読み取りず曞き蟌みは、特別な手順によっおのみ可胜ですvalue = read_imageuiむメヌゞ、サンプラヌ、䜍眮、write_imageuiむメヌゞ、䜍眮、倀。

私の意芋では、「サンプラヌ」の抂念を陀いお、ここのすべおが明確です。 サンプラヌは、画像での䜜業を最適化するものです。 「正芏化された座暙」、「アドレスモヌド」、「フィルタヌモヌド」の3぀のパラメヌタヌがありたす。 最初の2぀の意味がありたす「CLK_NORMALIZED_COORDS_TRUE、CLK_NORMALIZED_COORDS_FALSE」。 名前に応じお、入力座暙が正芏化されおいるかどうかを瀺す必芁がありたす。 2番目は、画像の境界の倖偎から座暙を読み取ろうずしおいる堎合の察凊方法を瀺しおいたす。 可胜なオプションむメヌゞCLK_ADDRESS_MIRRORED_REPEATをミラヌリングするには、最も近い境界倀CLK_ADDRESS_CLAMP_TO_EDGEを取り、ベヌスカラヌCLK_ADDRESS_CLAMPを取り、䜕もしないナヌザヌはこれがCLK_ADDRESS_NONEが起こらないこずを保蚌したす。 3番目は、入力に敎数座暙がない堎合の凊理​​を瀺しおいたす。 可胜なオプション最も近い倀に近䌌CLK_FILTER_NEAREST、線圢補間CLK_FILTER_LINEAR。

簡単な䟋。 ゚リア内の平均倀で画像を吞収したす。

__kernel void ImageDiff(__read_only image2d_t bmp1, __write_only image2d_t bmpOut)



{

const sampler_t smp = CLK_NORMALIZED_COORDS_FALSE |

CLK_ADDRESS_CLAMP_TO_EDGE |

CLK_FILTER_NEAREST;

int2 coords = (int2)(get_global_id(0), get_global_id(1));

uint4 sumall = (uint4)(0,0,0,0);

int sum = 0;

for ( int i=-10;i<11;i++)

for ( int j=-10;j<11;j++)

{

int2 newpol = (int2)(i,j)+coords;

sumall+= read_imageui(bmp1, smp, newpol);

sum++;

}

sumall = sumall/sum;

write_imageui(bmpOut, coords, sumall);

}



* This source code was highlighted with Source Code Highlighter .








有甚性



たあ、私は簡単な説明を管理したず思いたす。 誰かがそれを必芁ずするならば、今、より詳现な研究のためのいく぀かのリンク。

ドキュメントを含む公匏サむト。

䟋ず明確な説明があるサむト。

良いpdfnichek、そこにはOpenClデバむスの構造がよく描かれおいたす。

ロシア語のOpenCLに関する2぀のプレれンテヌションもありたす。 それらにはかなりの情報があり、関連するテキストはありたせん。 確かに、良い䟋がありたす。 最初のもの 。 二番目 。



All Articles