忍者ブログ

Memeplexes

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

C#でOpenCL入門 チュートリアルその2 デバイス

 OpenCLでは並列計算をしてくれるハードウェアのことをデバイスといいます。
例外はあるもののこれはまずGPUのことだと考えて間違い有りません(実はCPUでもいいのですが)。
デバイス=GPUです。

GPUで並列計算するためには、下準備としてPCに付いているGPUを列挙する必要があります。
今回はGPU、OpenCLの用語で言うとデバイスを列挙、情報を表示する方法をメモします。


プラットフォームからデバイスを取得

前回出てきたプラットフォームは、GPUのメーカーを表すようなものでした。
今回扱うデバイスは、そのメーカーの作ったGPUのことです。
プラットフォームから、デバイスを取得できるのです(生産者から製品を)。

それにはclGetDeviceIDs関数を使います。
参考

cl_int clGetDeviceIDs(
    cl_platform_id platform,
    cl_device_type device_type,
    cl_uint num_entries,
    cl_device_id *devices,
    cl_uint *num_devices
)

platformは取得するデバイスのプラットフォームです。
device_typeは取得するデバイスの種類。具体的には以下の5つから選びます。

cl_device_type 解説
CL_DEVICE_TYPE_CPU (1 << 1) CPUです。
CL_DEVICE_TYPE_GPU (1 << 2) GPUです。
CL_DEVICE_TYPE_ACCELERATOR (1 << 3) IBM CELL(PS3に使われている)などです。
CL_DEVICE_TYPE_DEFAULT (1 << 0) デフォルトのデバイスです。
CL_DEVICE_TYPE_ALL 0xFFFFFFFF 使用可能な全てのOpenCLデバイスです。


num_entriesは結果を格納する引数devicesの配列の長さです。もしdevicesがNULLでなかったら、この引数は0より大きくなければいけません。
devicesはこの関数の結果を格納します。もしこの値がNULLなら、この引数は無視されます。
num_devicesは使用可能なデバイスの数を返します。この値がNULLならば、この引数は無視されます。

戻り値
名前 解説
CL_SUCCESS 0 関数が成功しました。
CL_INVALID_PLATFORM -32 platformが無効な値です。
CL_INVALID_DEVICE_TYPE -31 device_typeが無効な値です。
CL_INVALID_VALUE -30 num_entriesが0なのにdevicesがNULLではありません。あるいは、num_devicesとdevicesが両方共NULLです。
CL_DEVICE_NOT_FOUND -1 device_typeで指定されたタイプのデバイスが存在しません。
CL_OUT_OF_RESOURCES -5 デバイスでリソースを確保するのに失敗しました。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。


デバイスの列挙

では早速clGetDeviceIDs()を使ってみましょう。
システムにインストールされたGPU(正確にはデバイス)を列挙します。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            foreach (var device in getDevices(platform, DeviceType.Default))
            {
                Console.WriteLine(device);
            }
        }
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }

    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 clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );
}

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}
 

実行結果は、私の環境ではこうなりました:

82646520
108450576

うーん意味不明ですね。
これが意味するのはシステムにGPUが2つ付いているということです。
他の環境では数字は一つしか表示されないかもしれません。

しかし数字が表示されるだけというのはなんとも無機質です。
GPUの名前が表示されたらいいのですが…


デバイスの情報

デバイスのIDからその名前や各種情報を取得する関数があります。
それがclGetDeviceInfo()関数です。
参考

cl_int clGetDeviceInfo(
    cl_device_id device,
    cl_device_info param_name,
    size_t param_value_size,
    void *param_value,
    size_t *param_value_size_ret
)

deviceは情報を取得するデバイスです。clGetDeviceIDs関数によって返された値を使います。
param_nameは情報のタイプを表す列挙型です。膨大な種類があります。詳細は以下の表に。
param_value_sizeはparam_valueにセットするメモリのサイズです。
param_valueはこの関数の結果を格納するメモリです。
param_value_size_retは情報の実際のサイズです。NULLならば無視されます。

cl_device_info  値  
 CL_DEVICE_ADDRESS_BITS cl_uint  0x100D  
 CL_DEVICE_AVAILABLE cl_bool  0x1027  
 CL_DEVICE_COMPILER_AVAILABLE cl_bool  0x1028  
 CL_DEVICE_DOUBLE_FP_CONFIG cl_device_fp_config  0x1032  
 CL_DEVICE_ENDIAN_LITTLE cl_bool  0x1026  
 CL_DEVICE_ERROR_CORRECTION_SUPPORT cl_bool  0x1024  
 CL_DEVICE_EXECUTION_CAPABILITIES cl_device_exec_capabilities  0x1029  
 CL_DEVICE_EXTENSIONS char[]  0x1030  
 CL_DEVICE_GLOBAL_MEM_CACHE_SIZE cl_ulong  0x101E  
 CL_DEVICE_GLOBAL_MEM_CACHE_TYPE cl_device_mem_cache_type  0x101C  
 CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE cl_uint  0x101D  
 CL_DEVICE_GLOBAL_MEM_SIZE cl_ulong  0x101F  
 CL_DEVICE_HALF_FP_CONFIG cl_device_fp_config  0x1033  
 CL_DEVICE_HOST_UNIFIED_MEMORY cl_bool  0x1035  
 CL_DEVICE_IMAGE_SUPPORT cl_bool  0x1016  
 CL_DEVICE_IMAGE2D_MAX_HEIGHT size_t  0x1012  
 CL_DEVICE_IMAGE2D_MAX_WIDTH size_t  0x1011  
 CL_DEVICE_IMAGE3D_MAX_DEPTH size_t  0x1015  
 CL_DEVICE_IMAGE3D_MAX_HEIGHT size_t  0x1014  
 CL_DEVICE_IMAGE3D_MAX_WIDTH size_t  0x1013  
 CL_DEVICE_LOCAL_MEM_SIZE cl_ulong  0x1023  
 CL_DEVICE_LOCAL_MEM_TYPE cl_device_local_mem_type  0x1022  
 CL_DEVICE_MAX_CLOCK_FREQUENCY cl_uint  0x100C  
 CL_DEVICE_MAX_COMPUTE_UNITS cl_uint  0x1002  
 CL_DEVICE_MAX_CONSTANT_ARGS cl_uint  0x1021  
 CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE cl_ulong  0x1020  
 CL_DEVICE_MAX_MEM_ALLOC_SIZE cl_ulong  0x1010  
 CL_DEVICE_MAX_PARAMETER_SIZE size_t  0x1017  
 CL_DEVICE_MAX_READ_IMAGE_ARGS cl_uint  0x100E  
 CL_DEVICE_MAX_SAMPLERS cl_uint  0x1018  
 CL_DEVICE_MAX_WORK_GROUP_SIZE size_t  0x1004  
 CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS cl_uint  0x1003  
 CL_DEVICE_MAX_WORK_ITEM_SIZES size_t[]  0x1005  
 CL_DEVICE_MAX_WRITE_IMAGE_ARGS cl_uint  0x100F  
 CL_DEVICE_MEM_BASE_ADDR_ALIGN cl_uint  0x1019  
 CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE cl_uint  0x101A  
 CL_DEVICE_NAME char[]  0x102B  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR cl_uint  0x1036  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT cl_uint  0x1037  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_INT cl_uint  0x1038  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG cl_uint  0x1039  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT cl_uint  0x103A  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE cl_uint  0x103B  
 CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF cl_uint  0x103C  
 CL_DEVICE_OPENCL_C_VERSION char[]  0x103D  
 CL_DEVICE_PLATFORM  cl_platform_id  0x1031  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR  cl_uint  0x1006  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT  cl_uint  0x1007  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT  cl_uint  0x1008  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG  cl_uint  0x1009  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT  cl_uint  0x100A  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE  cl_uint  0x100B  
 CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF cl_uint  0x1034  
 CL_DEVICE_PROFILE char[]   0x102E  
 CL_DEVICE_PROFILING_TIMER_RESOLUTION size_t   0x1025  
 CL_DEVICE_QUEUE_PROPERTIES cl_command_queue_properties   0x102A  
 CL_DEVICE_SINGLE_FP_CONFIG cl_device_fp_config   0x101B  
 CL_DEVICE_TYPE cl_device_type   0x1000  
 CL_DEVICE_VENDOR  char[]  0x102C  
 CL_DEVICE_VENDOR_ID  cl_uint  0x1001  
 CL_DEVICE_VERSION  char[]  0x102F  
 CL_DRIVER_VERSION  char[]  0x102D  

戻り値
名前 解説
CL_SUCCESS 0 関数は成功しました
CL_INVALID_DEVICE -33 deviceが無効な値です。
CL_INVALID_VALUE -30 param_nameが無効な値であるか、param_value_sizeが小さすぎ、param_valueがNULLではありません。
CL_OUT_OF_RESOURCES -5 デバイスでリソースを確保するのに失敗しました。
CL_OUT_OF_HOST_MEMORY -6 メモリが足りません。




デバイス名の列挙

先程はデバイスのIDを列挙しました。
ここではデバイスの名前を列挙します。

Program.cs
using System;

class Program
{
    static void Main()
    {
        foreach (var platform in getPlatforms())
        {
            foreach (var device in getDevices(platform, DeviceType.Default))
            {
                Console.WriteLine(getDeviceInfo(device, DeviceInfoString.Name));
            }
        }
    }

    private static string getDeviceInfo(IntPtr device, DeviceInfoString deviceInfoName)
    {
        int valueSize;
        OpenCLFunctions.clGetDeviceInfo(device, deviceInfoName, 0, null, out valueSize);
        System.Text.StringBuilder result = new System.Text.StringBuilder(valueSize);
        OpenCLFunctions.clGetDeviceInfo(device, deviceInfoName, valueSize, result, out valueSize);
        return result.ToString();
    }

    private static IntPtr[] getDevices(IntPtr platform, DeviceType deviceType)
    {
        int deviceCount;
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, 0, null, out deviceCount);
       
        IntPtr[] result = new IntPtr[deviceCount];
        OpenCLFunctions.clGetDeviceIDs(platform, deviceType, deviceCount, result, out deviceCount);
        return result;
    }


    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 clGetDeviceIDs(
        IntPtr platform,
        DeviceType deviceType,
        int entryCount,
        IntPtr[] devices,
        out int deviceCount
        );

    [DllImport("OpenCL.dll")]
    public static extern int clGetDeviceInfo(
        IntPtr device, 
        DeviceInfoString paramName, 
        int paramValueSize,
        System.Text.StringBuilder paramValue,
        out int paramValueSizeReturn
        );
    
}

enum DeviceType:long
{
    Default = (1 << 0),
    Cpu = (1 << 1),
    Gpu = (1 << 2),
    Accelerator = (1 << 3),
    All = 0xFFFFFFFF
}


enum DeviceInfoString
{
    Name = 0x102B,
    Vendor = 0x102C,
    DriverVersion = 0x102D,
    Profile = 0x102E,
    Version = 0x102F,
    Extensions = 0x1030
}


このプログラムはPCに付いているGPUの名前を列挙します。
環境によって実行結果は異なります。
私の環境ではこうなりました:
 
GeForce GTX 260
Juniper
 
GeForce GTX 260というのはNVIDIA社製のGPUの名前です。
JuniperというのはAMDのGPU、Radeon HD 5700シリーズの通称です。


拍手[2回]

PR