C#でのOpenCLはただ

OpenCLテクノロジーは2008年に登場しましたが、これまでのところあまり配布されていません。 テクノロジーの利点は間違いありません:計算の高速化、クロスプラットフォーム、GPUとCPUの両方でコードを実行する機能、この標準は多くの企業(Apple、AMD、Intel、nVidiaなど)によってサポートされています。 多くの欠点はありませんが、それらは次のとおりです。CUDAを使用するよりもnVidiaでの作業が遅い、使用が複雑です。 最初のマイナスは、プログラムの速度がクロスプラットフォームよりも重要である深刻な開発にのみ影響します。 2番目は、特定の開発方法を選択する開発者にとっての主な障害です。 多数のヘッダー、ドライバー、および標準を理解するのに多くの時間がかかります。 しかし、すべてがそれほど悪いわけではありません。 この記事のトピックは、C#でOpenCLを最も簡単な方法で実行し、並列プログラミングを楽しむ方法についての短いガイドになります。



ドライバーのセットアップ


最も長くて難しい部分は、OpenCL用のドライバーのセットアップです。 システムがコンピューターに1年以上使用されている場合、AMDカードがあり、システムのドライバーが更新されていることをすぐに言います。システムの再配置が必要になる可能性があります。

AMD
AMDにはドライバーに関する問題がいくつかありますが、多くの例が含まれたすばらしいSDKがあり、サポートフォーラムは稼働中です。 AMD Catalystの最近のバージョンでは、OpenCLのドライバーは自動的にインストールされます。 SDK for workはこちらから入手できます 。 インストールする前に、グラフィックカード OpenCLをサポートしているかどうかを確認してください。 AMDの場合、部分的なサポートは4300シリーズから始まり、完全なサポートは5400から始まります。

nVidia
原則として、nVidiaのドライバーの問題は少ないですが、時々起きることもあります。 OpenCLのサポートは、8600シリーズ前後から提供されます。 ここからドライバーを入手できます

Intel
AMDやnVidiaとは異なり、Intelドライバーで問題が発生したことはありません。 ここで取ることができます



システムにドライバーをインストールしても、ドライバーが機能することは保証されません。 AMDのバグの数は圧倒的です(3台のコンピューターのうち2台にインストールするときに問題が発生します)。nVidiaの場合は大きい(1/3)。 したがって、インストール後、まずOpenCLが接続されているかどうかを確認することをお勧めします。 これは、ビデオカードのパラメータを表示するプログラムを介して最も簡単に実行できます。 私はGpuCapsViewerを使用し、opencl-zとGPU-Zも動作します。

問題が発生した場合...すべての古いドライバーを削除し、再配置します。 AMDの場合-正しいバージョンのドライバーをインストールしてください。 ラップトップドライバーは、多くの場合バグがあります。 問題が解消されていない場合は、Windowsを再インストールすると問題が解決します。



ラッパー


私たちの目標は、C#のOpenCLで可能な限り単純なプログラムを作成することなので、倒錯を処理せずOpenCLヘッダーを接続しますが、開発を簡素化する既製のライブラリを使用します。 私のように、最も完全でバグの多いバージョンは、 OpenTKの一部であるcloo.dllです。 最も使いやすく 、多くの操作を自動化するのはOpenCLTemplateで 、これはclooのアドオンです。 後者のマイナス面のうち、AMD、たとえば最新のドライバー(11.6)を使用する場合、デバイスの初期化を拒否することがあります。 OpenSourceプロジェクト以来、私が抱えていた不具合を見つけて修正しましたが、いつライブラリの新しいバージョンがリリースされるのかわかりません。 また、インターネット上で見つけることができるあまり知られていないラッパーもいくつかあります。



最初のプログラム


最初のプログラムとして、OpenCLを使用して2つのベクトルv1とv2の合計を計算します。 cloo.dllを使用して記述されたプログラム例:



クロプログラム



private void button4_Click( object sender, EventArgs e)

{

// , . Platforms[1]

//

ComputeContextPropertyList Properties = new ComputeContextPropertyList(ComputePlatform.Platforms[1]);

ComputeContext Context = new ComputeContext(ComputeDeviceTypes.All, Properties, null , IntPtr .Zero);



// , (GPU CPU).

// . , C99 OpenCL.

string vecSum = @"

__kernel void

floatVectorSum(__global float * v1,

__global float * v2)

{

int i = get_global_id(0);

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

}



"
;

// , vecSum

List <ComputeDevice> Devs = new List <ComputeDevice>();

Devs.Add(ComputePlatform.Platforms[1].Devices[0]);

Devs.Add(ComputePlatform.Platforms[1].Devices[1]);

Devs.Add(ComputePlatform.Platforms[1].Devices[2]);

// vecSum

ComputeProgram prog = null ;

try

{



prog = new ComputeProgram(Context, vecSum); prog.Build(Devs, "" , null , IntPtr .Zero);

}



catch



{ }



//

ComputeKernel kernelVecSum = prog.CreateKernel( "floatVectorSum" );



// , .

float [] v1 = new float [100], v2 = new float [100];

for ( int i = 0; i < v1.Length; i++)

{

v1[i] = i;

v2[i] = 2 * i;

}

// .

ComputeBuffer< float > bufV1 = new ComputeBuffer< float >(Context, ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer, v1);

ComputeBuffer< float > bufV2 = new ComputeBuffer< float >(Context, ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer, v2);

// vecSum

kernelVecSum.SetMemoryArgument(0, bufV1);

kernelVecSum.SetMemoryArgument(1, bufV2);

// . , !

ComputeCommandQueue Queue = new ComputeCommandQueue(Context, Cloo.ComputePlatform.Platforms[1].Devices[0], Cloo.ComputeCommandQueueFlags.None);

//. Execute - vecSum (v1.Length)

Queue.Execute(kernelVecSum, null , new long [] { v1.Length }, null , null );

// .

float [] arrC = new float [100];

GCHandle arrCHandle = GCHandle.Alloc(arrC, GCHandleType.Pinned);

Queue.Read< float >(bufV1, true , 0, 100, arrCHandle.AddrOfPinnedObject(), null );

}




* This source code was highlighted with Source Code Highlighter .








以下は、OpenCLTemplate.DLLを介して実装された同じプログラムです。




private void btnOpenCL_Click( object sender, EventArgs e)

{

// , (GPU CPU).

// . , C99 OpenCL.

string vecSum = @"

__kernel void

floatVectorSum(__global float * v1,

__global float * v2)

{

int i = get_global_id(0);

v1[i] = v1[i] * v2[i];

}"
;



// . . GPU.

//OpenCLTemplate.CLCalc.InitCL(Cloo.ComputeDeviceTypes.All)

//GPU CPU.

OpenCLTemplate.CLCalc.InitCL();

// .

List <Cloo.ComputeDevice> L = OpenCLTemplate.CLCalc.CLDevices;

//

OpenCLTemplate.CLCalc.Program.DefaultCQ = 0;

// vecSum

OpenCLTemplate.CLCalc.Program.Compile( new string [] { vecSum });

// , .

OpenCLTemplate.CLCalc.Program.Kernel VectorSum = new OpenCLTemplate.CLCalc.Program.Kernel( "floatVectorSum" );

int n = 100;



float [] v1 = new float [n], v2 = new float [n], v3 = new float [n];



// , .

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

{

v1[i] = i;

v2[i] = i*2;

}





//

OpenCLTemplate.CLCalc.Program.Variable varV1 = new OpenCLTemplate.CLCalc.Program.Variable(v1);

OpenCLTemplate.CLCalc.Program.Variable varV2 = new OpenCLTemplate.CLCalc.Program.Variable(v2);



// ,

OpenCLTemplate.CLCalc.Program.Variable[] args = new OpenCLTemplate.CLCalc.Program.Variable[] { varV1, varV2 };



//

int [] workers = new int [1] { n };



// VectorSum args workers

VectorSum.Execute(args, workers);



//

varV1.ReadFromDeviceTo(v3);



}




* This source code was highlighted with Source Code Highlighter .






ご覧のとおり、2番目のオプションははるかにシンプルで直感的です。



最後のリンク


まず、このサイトはC# -www.cmsoft.com.brを介したOpenCLでのプログラミングに専念しています。 残念ながら、それを導く人はほとんどいないので、例はしばしば不適切であり、サイトの作者によって作成されたOpenCLTemplateは非常にバグが多いです。

便利な場所は、 www.opentk.comのサイトです。そこでは、cloo.dllを介してプログラミングに関する質問に非常に迅速に回答します

sourceforge.net/projects/clooでほぼ同じ答えを得ることができます

C99ベースのOpenClプログラミング標準については、 www.khronos.org / openclで説明しています



OpenClの概要 」という記事の続きでは、ビデオカードのプログラミングに使用するプログラミング言語の機能について説明しています。



All Articles