忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

C#でOpenCL入門 チュートリアルその1 プラットフォーム

 ググッてみたところOpenCLの入門というかサンプルは数が少ないようなので
自分で書いたコードをメモしておきます。

OpenCLはGPUを使って並列計算をしようというフレームワークです。
今まで3DCGを描くことくらいにしか使っていなかったハードウェアを、並列計算にも応用してやろうというあれです。
類似の技術にMSのコンピュートシェーダーやATI Stream、NVIDIA CUDAがあります。
このうちコンピュートシェーダーはこのブログでも以前取り上げましたね。

OpenCLはクロノスグループという団体が関わっているようです。
C言語やC++でのインターフェースが決められています。
MSではないので、当然のごとくC#からは簡単には使えません。
P/Invokeを通して使う必要があります。
せっかくなのでここではC#から使ってみます。

インストール

いくつかのメーカーからのSDKがあるとおもうのですが、ここでは例としてAMDのものを紹介します。
amdPage.JPG

Overviewの下の方にdownload hereというのがあるのでそこをクリック

amdDownloadPage.JPG
ダウンロードのリンクが出てきます。
ダウンロードした後はインストーラを起動します。


プラットフォーム

今回はプラットフォームをプログラムから操作する方法をメモします。
GPUを動かして計算するのはまた次回以降で。

OpenCLを使うには、GPUが必要です。
正確に言うとCPUでもいいのですが、普通はGPUです。
GPUのメーカーがOpenCLに対応した環境を用意しています。
それがOpenCLで言うプラットフォームです。
次のようなものがあります。

NVIDIA CUDA
AMD Accelerated Parallel Processing


NVIDIAのGPUを使っている人は「NVIDIA CUDA」というプラットフォームがあるでしょう(インストールすれば)。
AMDのGPUを使っている人は「AMD Accelerated Parallel Processing」というプラットフォームがあるでしょう(インストールすれば)。
私は一つのパソコンに2つ両方を使っているので(これは本当はいけないことなのかもしれませんが)、上の2つが両方共ありました。

どういうプラットフォームがインストールされているのか確かめる方法ですが、それにはプログラムから調べます。
(もっと簡単な方法もありそうな気もしますが)

インストールされたプラットフォームは、clGetPlatformIDs関数を使って取得します。
参考

cl_int clGetPlatformIDs(
    cl_uint num_entries,
    cl_platform_id *platforms,
    cl_uint *num_platforms
    );

cl_XXXというのはOpenCLが定義している型です。
cl_は無視してXXXだけ見ると意味が取れます。
たとえばcl_intは実はただのintです。

引数は3つあります。
num_entriesはplatforms配列の長さ。platformsがNULLでないならば、この値は0より大でなければいけません。
platformsは、取得するプラットフォームを格納する配列。NULLならば無視されます。必要な配列の長さはnum_platformsで取得できます。
num_platformsは、存在するプラットフォームの数を返します。NULLならば無視されます。

戻り値は3つのうちの一つです。

名前 数値 説明
CL_SUCCESS 0 関数が成功しました。
CL_INVALID_VALUE -30 関数が失敗しました。num_entriesが0で、platformsがNULLではありません。あるいはnum_platformsとplatformsの両方がNULLです。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。



プラットフォームの個数を取得する

まずはclGetPlatformIDsの簡単な使い方から見てみましょう。
次のようにすると、パソコンにインストールされているOpenCLプラットフォームの個数を取得できます。

Program.cs
using System;

class Program
{
    static void Main()
    {
        int platformCount = 0;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);
        Console.WriteLine(platformCount);
    }
}
 

OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);
}
 

実行結果は環境によって異なります。
普通は「1」でしょうが、私の環境では「2」になりました。
NVIDIAとAMDのGPU両方がついており、両方の環境がインストールされているからです。
(「1」が普通です。「2」にならないからと言って心配しなくても構いません)


プラットフォームを取得する

プラットフォーム(のID)そのものを取得するには次のようにします。
上で取得した個数を元に、その長さの配列を用意し、それを引数として渡すのです。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            Console.WriteLine(platform);
        }
    }

    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 

実行結果は

111875472
125673668

でした。
意味がわかりませんね。
IntPtrの値をそのまま出力しているからです。

この値はプラットフォームのIDです。
このIDを通して、他の関数を使ってプラットフォームの意味のある情報を取得することが出来ます。

くどいようですが、普通の環境では数字は1つしか表示されないはずです。
プラットフォームは普通一つしかインストールしないからです(たぶん)。


プラットフォームの情報

上の例が意味不明なのは、IDをそのまま表示しているからです。
意味があるものにするには、たとえばプラットフォームの名前が表示されると嬉しいですね。
プラットフォームの情報を取得する必要があります。
そのための関数が、clGetPlatformInfo関数です。
参考

cl_int clGetPlatformInfo(
    cl_platform_id platform,
    cl_platform_info param_name,
    size_t param_value_size,
    void *param_value,
    size_t *param_value_size_ret
)

platformはプラットフォームのIDです。clGetPlatformIDsを使って取得したIDです。この値はNULLでもいいようですが、NULLの場合の振る舞いは実装に依存します。
param_nameは列挙型の値で、この関数で取得するプラットフォームの情報のタイプを表します。どんな値を取れるかは下のテーブルでに書いておきます。
param_value_sizeはparam_valueで指されているメモリのサイズ(バイト単位)を表します。
param_valueはプラットフォームの情報を返します。この値はNULLならば無視されます。
param_value_size_retは、プラットフォームの情報のサイズを返します(param_valueのメモリのサイズです。)。NULLならば無視されます。

cl_platform_info 解説
CL_PLATFORM_PROFILE 0x0900  OpenCLのプロファイル文字列です。実装によってサポートされているプロファイル名を表します。この値は次のうちのどれか一つになります。

FULL_PROFILEはOpenCLの仕様を実装が満たしていることを意味します。

EMBEDDED_PROFILEは、実装がOpenCLのサブセットを満たしているという意味です。
CL_PLATFORM_VERSION 0x0901  OpenCLのバージョンです。
CL_PLATFORM_NAME 0x0902  プラットフォームの名前です。
CL_PLATFORM_VENDOR 0x0903  ベンダー名です。
CL_PLATFORM_EXTENSIONS 0x0904  拡張名のリストを返します。それぞれの拡張はスペースで区切られています。

戻り値
名前 数値 解説
CL_SUCCESS 0 関数が成功しました。
CL_INVALID_PLATFORM -32 platformが有効な値ではありません。
CL_INVALID_VALUE -30 param_nameが有効な値でないか、あるいはparam_value_sizeが戻り値の型のサイズより小さくparam_valueがNULLではありません。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。

プラットフォームの名前を列挙

ではclGetPlatformInfo関数を使ってプラットフォームの名前を列挙してみましょう。
次のようなプログラムになります。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            Console.WriteLine(getPlatformInfo(platform, PlatformInfo.Name));
        }
    }

    private static string getPlatformInfo(IntPtr platform, PlatformInfo platformInfo)
    {
        int parameterValueSize;
        OpenCLFunctions.clGetPlatformInfo(
            platform,
            platformInfo,
            0,
            null,
            out parameterValueSize
            );

        System.Text.StringBuilder result = new System.Text.StringBuilder(parameterValueSize);
        OpenCLFunctions.clGetPlatformInfo(
            platform,
            platformInfo,
            parameterValueSize,
            result,
            out parameterValueSize
            );
        return result.ToString();
    }

    private static IntPtr[] getPlatforms()
    {
        int platformCount;
        OpenCLFunctions.clGetPlatformIDs(0, null, out platformCount);

        IntPtr[] result = new IntPtr[platformCount];
        OpenCLFunctions.clGetPlatformIDs(platformCount, result, out platformCount);
        return result;
    }
}
 

OpenCLFunctions.cs
using System;
using System.Runtime.InteropServices;

static class OpenCLFunctions
{
    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformIDs(int entryCount, IntPtr[] platforms, out int platformCount);

    [DllImport("OpenCL.dll")]
    public static extern int clGetPlatformInfo(
        IntPtr platform,
        PlatformInfo parameterName,
        int parameterValueSize,
        System.Text.StringBuilder parameterValue,
        out int parameterValueSizeReturn
        );
}

enum PlatformInfo
{
    Profile = 0x0900,
    Version = 0x0901,
    Name = 0x0902,
    Vendor = 0x0903,
    Extensions = 0x0904
}
 

このプログラムはPCにインストールされているOpenCLのプラットフォームを列挙していきます。
私の環境では実行結果はこうなりました:

NVIDIA CUDA
AMD Accelerated Parallel Processing

くどいようですが、普通はこの2つのうちのどちらか片方だけが表示されるのではないかと思います。
2つの違うメーカーのGPUをPCに付けているときにはこのように2つ表示されますが、そうでないときには1つだけでしょう(たぶん)。

拍手[8回]

PR