10.4. 組み込み関数

OpenCL-C組込み関数はstdio.hやstdlib.h、math.hといった標準ライブラリをインクルードできないので、それらの代用という面もありますが、ベクトルや並列演算、FPU等のハードウェアに合わせた機能を提供します。

10.4.1. ワークアイテム関数

注記

詳しくは「表:ワークアイテム・グループ関数」(表B.102「表:ワークアイテム・グループ関数」)を参照ください。

get_global_id関数はdimindexで指定した次元で固有のグローバル・ワークアイテムIDを返します。

定義. 

size_t get_global_id(uint dimindx)

サンプル. 

size_t global_id_0 = get_global_id(0);
size_t global_id_1 = get_global_id(1);
size_t global_id_2 = get_global_id(2);

get_local_id関数はdimindexで指定した次元で固有のローカル・ワークアイテムIDを返します。

size_t get_global_offset(uint dimindx)

dimindexで指定した次元のオフセット(offset)を返します。clEnqueueNDRangeKernelのglobal_work_offset引数で指定したものが該当します。

size_t get_local_id(uint dimindx)

dimindexで指定した次元で固有のローカル・ワークアイテム(work item)IDを返します。

size_t get_group_id(uint dimindx)

dimindexで指定した次元のオフセット(offset)を返します。clEnqueueNDRangeKernelのglobal_work_offset引数で指定したものが該当します。

size_t get_global_size(uint dimindx)

dimindexで指定した次元のローカル・ワークアイテム(work item)の数を返します。clEnqueueNDRangeKernelのlocal_work_size引数で指定したものが該当します。

size_t get_local_size(uint dimindx)

dimindexで指定した次元のローカル・ワークアイテム(work item)の数を返します。clEnqueueNDRangeKernelのlocal_work_size引数で指定したものが該当します。

size_t get_num_groups(uint dimindx)

dimindexで指定した次元のワークグループ(work group)の数を返します。clEnqueueNDRangeKernelのlocal_work_size引数で指定したものが該当します。

__kernel void ndrange_id(__global write_only float *output) {

   size_t global_id_0 = get_global_id(0); //(1)
   size_t global_id_1 = get_global_id(1); //(2)
   size_t global_id_2 = get_global_id(2); //(3)

   size_t global_size_0 = get_global_size(0); //(4)
   size_t global_size_1 = get_global_size(1); //(5)

   size_t offset_0 = get_global_offset(0); //(6)
   size_t offset_1 = get_global_offset(1); //(7)
   size_t offset_2 = get_global_offset(2); //(8)

   size_t local_id_0 = get_local_id(0); //(9)
   size_t local_id_1 = get_local_id(1); //(10)
   size_t local_id_2 = get_local_id(2); //(11)

   int index_0 = global_id_0 - offset_0; //(12)
   int index_1 = global_id_1 - offset_1; //(13)
   int index_2 = global_id_2 - offset_2; //(14)

   int index = index_2 * global_size_0 * global_size_1  + index_1 * global_size_0 + index_0;
   float f = global_id_0 * 100.0f + global_id_1 * 10.0f + global_id_2 * 1.0f;
   f += local_id_0 * 0.1f + local_id_1 * 0.01f + local_id_2 * 0.001f;
   output[index] = f; //(15)
}

(1)

次元1のグローバルIDを取得

(2)

次元2のグローバルIDを取得

(3)

次元3のグローバルIDを取得

(4)

次元1のグローバルサイズ(ワークアイテム数)を取得

(5)

次元2のグローバルサイズ(ワークアイテム数)を取得

(6)

次元1のグローバルオフセットを取得

(7)

次元2のグローバルオフセットを取得

(8)

次元3のグローバルオフセットを取得

(9)

次元1のローカルIDを取得

(10)

次元2のローカルIDを取得

(11)

次元3のローカルIDを取得

(12)

グローバルIDとオフセットを調整

(13)

グローバルIDとオフセットを調整

(14)

グローバルIDとオフセットを調整

(15)

出力変数「output」に代入

ホストプログラム側の設定は、64個のワークアイテムを3次元に分割するものとします。

package com.book.jocl.workitem;
import static org.jocl.CL.*;

import java.io.File;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Scanner;

import org.jocl.Pointer;
import org.jocl.Sizeof;
import org.jocl.cl_command_queue;
import org.jocl.cl_context;
import org.jocl.cl_device_id;
import org.jocl.cl_kernel;
import org.jocl.cl_mem;
import org.jocl.cl_platform_id;
import org.jocl.cl_program;

public class WorkItemTest {

        private static final String KERNEL_PATH = "ndrange_id.cl";
        private static final String KERNEL = "ndrange_id";

        private static void print_error(String src_msg, int err)
        {
            final String[] err_msg = new String[]{
                "CL_SUCCESS",
                "CL_DEVICE_NOT_FOUND",
                "CL_DEVICE_NOT_AVAILABLE",
                "CL_COMPILER_NOT_AVAILABLE",
                "CL_MEM_OBJECT_ALLOCATION_FAILURE",
                "CL_OUT_OF_RESOURCES",
                "CL_OUT_OF_HOST_MEMORY",
                "CL_PROFILING_INFO_NOT_AVAILABLE",
                "CL_MEM_COPY_OVERLAP",
                "CL_IMAGE_FORMAT_MISMATCH",
                "CL_IMAGE_FORMAT_NOT_SUPPORTED",
                "CL_BUILD_PROGRAM_FAILURE",
                "CL_MAP_FAILURE",
                "CL_MISALIGNED_SUB_BUFFER_OFFSET",
                "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST",
                "CL_COMPILE_PROGRAM_FAILURE     ",
                "CL_LINKER_NOT_AVAILABLE",
                "CL_LINK_PROGRAM_FAILURE",
                "CL_DEVICE_PARTITION_FAILED",
                "CL_KERNEL_ARG_INFO_NOT_AVAILABLE",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "CL_INVALID_VALUE",
                "CL_INVALID_DEVICE_TYPE",
                "CL_INVALID_PLATFORM",
                "CL_INVALID_DEVICE",
                "CL_INVALID_CONTEXT",
                "CL_INVALID_QUEUE_PROPERTIES",
                "CL_INVALID_COMMAND_QUEUE",
                "CL_INVALID_HOST_PTR",
                "CL_INVALID_MEM_OBJECT",
                "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR",
                "CL_INVALID_IMAGE_SIZE",
                "CL_INVALID_SAMPLER",
                "CL_INVALID_BINARY",
                "CL_INVALID_BUILD_OPTIONS",
                "CL_INVALID_PROGRAM",
                "CL_INVALID_PROGRAM_EXECUTABLE",
                "CL_INVALID_KERNEL_NAME",
                "CL_INVALID_KERNEL_DEFINITION",
                "CL_INVALID_KERNEL",
                "CL_INVALID_ARG_INDEX",
                "CL_INVALID_ARG_VALUE",
                "CL_INVALID_ARG_SIZE",
                "CL_INVALID_KERNEL_ARGS",
                "CL_INVALID_WORK_DIMENSION",
                "CL_INVALID_WORK_GROUP_SIZE",
                "CL_INVALID_WORK_ITEM_SIZE",
                "CL_INVALID_GLOBAL_OFFSET",
                "CL_INVALID_EVENT_WAIT_LIST",
                "CL_INVALID_EVENT",
                "CL_INVALID_OPERATION",
                "CL_INVALID_GL_OBJECT",
                "CL_INVALID_BUFFER_SIZE",
                "CL_INVALID_MIP_LEVEL",
                "CL_INVALID_GLOBAL_WORK_SIZE",
                "CL_INVALID_PROPERTY",
                "CL_INVALID_IMAGE_DESCRIPTOR",
                "CL_INVALID_COMPILER_OPTIONS",
                "CL_INVALID_LINKER_OPTIONS",
                "CL_INVALID_DEVICE_PARTITION_COUNT",
            };

            int index = -err;

            if (err != CL_SUCCESS) {

                System.out.printf("Failed Message: %s - Error Code: %d\n", src_msg, err, err_msg[index]);
                System.exit(-1);
            }

        }

        public static void main(String[] args) throws Exception {

                int err;

            /* OpenCL APIの変数 */
                cl_device_id[] device = new cl_device_id[1];
                cl_context context;
                cl_command_queue queue;
                cl_program program;
                cl_platform_id[] platform = new cl_platform_id[1];
                cl_kernel kernel;

                /*
                 * OpenCLデバイスのプラットフォームの特定
                 * 最初に見つけたプラットフォームを使用します。
                 * */
                err = clGetPlatformIDs(1, platform, null);

                /*
                 * CPU/GPUデバイスの情報取得をします。
                 * */
                err = clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_GPU, 1, device, null);
                if(err == CL_DEVICE_NOT_FOUND) {
                  err = clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_CPU, 1, device, null);
                }

                /* コンテキストの生成をします。 */
                context = clCreateContext(null, 1, device, null, null, null);


                /*
                 * OpenCL Cのソースコードをファイル(.cl)から
                 * 読み込みコンパイルします
                 * */

                /* ファイルを読み込みバッファーに投入します */
                StringBuffer sb = new StringBuffer();
                URL resource = WorkItemTest.class.getResource(KERNEL_PATH);
                String path = Paths.get(resource.toURI()).toFile().getAbsolutePath();
                System.out.println(path);
                Scanner sc = new Scanner(new File(path));
                while(sc.hasNext()) {
                        sb.append(sc.nextLine() + "\n");
                }
                /* programの生成(ファイルからOpenCL Cソースコードの読み込み) */
                int[] errPtr = new int[1];
        program = clCreateProgramWithSource(context,
                1, new String[]{sb.toString()}, null, errPtr);
        if(errPtr[0] < 0) {
                print_error("clCreateProgramWithSource",errPtr[0]);
        }
                /* programのビルド(コンパイル)をします。 */
        err = clBuildProgram(program, 0, null, null, null, null);
        if(err < 0) {
                print_error("clBuildProgram",err);
        }

                /* コマンドキューを生成します */
                queue = clCreateCommandQueue(context, device[0], 0, null);

                float[] result = new float[64];

                cl_mem res_buff = clCreateBuffer(
                                context,
                                CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
                                Sizeof.cl_float*64, Pointer.to(result), null);
                if(err < 0) {
                        print_error("clCreateBuffer", err);
                }

                kernel = clCreateKernel(program, KERNEL, null);

                clSetKernelArg(kernel, 0, Sizeof.cl_mem, Pointer.to(res_buff));

                int dim = 3;
                long[] global_offset = new long[]{0, 0, 0};
                long[] global_size = new long[]{4, 4, 4};
                long[] local_size = new long[]{2, 2, 2};

                err = clEnqueueNDRangeKernel(queue, kernel, dim, global_offset,
                      global_size, local_size, 0, null, null);
                if(err < 0) {
                        print_error("clCreateBuffer", err);
                }

                err = clEnqueueReadBuffer(queue, res_buff, CL_TRUE, 0,
                                        Sizeof.cl_float*64, Pointer.to(result), 0, null, null);
                if(err < 0) {
                        print_error("clEnqueueReadBuffer", err);
                }


                for(int i = 0;  i < 64; i++) {
                        System.out.printf("%f\n", result[i]);
                }

                clReleaseMemObject(res_buff);
                clReleaseKernel(kernel);
                clReleaseProgram(program);
            clReleaseCommandQueue(queue);
                clReleaseContext(context);

        }
}

出力. 

0.000000
100.099998
200.000000
300.100006
10.010000
110.110001
210.009995
310.109985
20.000000
120.099998
220.000000
320.100006
30.010000
130.110001
230.009995
330.109985
1.001000
101.100998
201.001007
301.101013
11.011000
111.111000
211.011002
311.110992
21.000999
121.100998
221.001007
321.101013
31.011000
131.110992
231.011002
331.110992
2.000000
102.099998
202.000000
302.100006
12.010000
112.110001
212.009995
312.109985
22.000000
122.099998
222.000000
322.100006
32.009998
132.110001
232.009995
332.109985
3.001000
103.100998
203.001007
303.101013
13.011000
113.111000
213.011002
313.110992
23.000999
123.100998
223.001007
323.101013
33.011002
133.110992
233.011002
333.110992

出力を見て頂くと、グローバル空間が4x4x4、ローカル空間が2x2x2ということが確認できます。

10.4.2. 数学関数

注記

詳細は「表:数学関数」(表B.103「表:数学関数」)を参照ください。

数学関数は組込み版のmath.hのようなものです。ベクトル型に対応しているので、math.hのアップグレード版と考えることもできます。math.hからの円滑な移行を可能するために設計された数学関数には、信号処理、統計、機械学習で頻繁に使うガンマ関数、三角関数、指数関数が提供されています。

関数定義. 

gentype cos (gentype)
gentype sin (gentype)
gentype tan (gentype)
gentype sincos (
    gentype x,
    __global gentype *cosval)

サンプル. 

float4 data = (float4)(M_PI_F,M_PI_2_F,M_PI_4_F,0.0f);
printf("data: %#v4f\n", data);
float4 res_cos = cos(data);
float4 res_sin = sin(data);
float4 res_sin_cos = res_sin/res_cos;
float4 res_tan = tan(data);

printf("cos(vecotr data) = %#v4f\n",res_cos);
printf("sin(vector data) = %#v4f\n",res_sin);
printf("sin/cos = %#v4f\n",res_sin_cos);
printf("tan = %#v4f\n", res_tan);
printf("cos(pi) = %f\n", cos(M_PI_F));
printf("cos(pi/2) = %f\n", cos(M_PI_2_F));
printf("cos(pi/4) = %f\n", cos(M_PI_4_F));

float angle = 45.0f;
float radians = angle * M_PI_F / 180.0f;
printf("45 degrees to radians, then cosine(r) = %f\n", cos(radians));
printf("pi/4 radians to degree %f\n", degrees(radians));

注記

M_PI_F等の定数については「表:浮動小数点マクロ」(表:浮動小数点マクロ)と「表:浮動小数点との互換性」(表B.106「表:浮動小数点との互換性」)と「表:浮動小数点定数」(表B.107「表:浮動小数点定数」)を参照ください。

出力. 

data: 3.141593,1.570796,0.785398,0.000000
cos(vecotr data) = -1.000000,-0.000000,0.707107,1.000000
sin(vector data) = -0.000000,1.000000,0.707107,0.000000
sin/cos = 0.000000,-22877334.000000,1.000000,0.000000
tan = 0.000000,-22877334.000000,1.000000,0.000000
cos(pi) = -1.000000
cos(pi/2) = -0.000000
cos(pi/4) = 0.707107
45 degrees to radians, then cosine(r) = 0.707107
pi/4 radians to degree 45.000000

注記

gentypeは「float, float2, float3, float4, float8, float16, double, double2, double3, double4, double8 or double16」のいずれかを指す。

双曲線余弦(hyperbolic cosine)、双曲線正弦(hyperbolic sine)、双曲線正接(Hyperbolic arc tangent)を計算するには以下の関数を使います。

関数定義. 

gentype cosh (gentype)
gentype sinh (gentype)
gentype tanh (gentype)

サンプル. 

float res_sinh = sinh(2.0f);
float res_cosh = cosh(2.0f);
float res_tanh = tanh(2.0f);

printf("sinh=%f, cosh=%f, tanh=%f\n", res_sinh, res_cosh, res_tanh);

出力. 

sinh=3.626860, cosh=3.762196, tanh=0.964028

逆双曲線余弦(Inverse hyperbolic cosine)、逆双曲線正弦(Inverse hyperbolic sine)、逆双曲線正接(Inverse Hyperbolic arc tangent)を計算するには以下の関数を使います。

関数定義. 

gentype acosh (gentype)
gentype asin (gentype)
gentype atanh (gentype)

サンプル. 

float res_asinh = asinh(3.626860f);
float res_acosh = acosh(3.762196f);
float res_atanh = atanh(0.964028f);

printf("asinh=%f, acosh=%f, atanh=%f\n", res_asinh, res_acosh, res_atanh);

出力. 

sinh=3.626860, cosh=3.762196, tanh=0.964028

xのy乗を計算するには、pow関数や、pown関数を使います。pownのy引数は整数型です。

gentype pow(gentype x, gentype y)
gentype pown(gentype x, intn y)

数値の丸めをする関数もあります。

gentype rint (gentype)

最近接偶数への丸め (round to the nearest even) モードで整数値への丸めを行います。

gentype ceil (gentype)

正の無限大への丸めモードで天井値を計算。

gentype round (gentype x)

丸め方向と関係なくxに最も近い整数値への丸め(中間値は0より遠い方向)を行います。

gentype trunc (gentype)

0への丸め (rounding toward zero)モードで整数への丸めを計算します。

サンプル. 

float2 tmp = (float2)(1.234f, 2.654f);

float2 rint_out = rint(tmp);
printf("rint: %#v2f\n", rint_out);
float2 round_out = round(tmp);
printf("round: %#v2f\n", round_out);
float2 trunc_out = trunc(tmp);
printf("trunc: %#v2f\n", trunc_out);
float2 floor_out = floor(tmp);
printf("floor: %#v2f\n", floor_out);
float2 ceil_out = ceil(tmp);
printf("ceil: %#v2f\n", ceil_out);

出力. 

rint: 1.000000,3.000000
round: 1.000000,3.000000
trunc: 1.000000,2.000000
floor: 1.000000,2.000000
ceil: 2.000000,3.000000

10.4.3. half型、native型数学関数

注記

詳細は「表:half型、native型の数学関数」(<<half_maths>)を参照ください。

half型とnative型に適合した数学関数です。half_func_name、native_func_nameというように、 half_ か native_ が関数の先頭文字となります。

half_が接頭にある場合は半精度浮動小数点を使い計算をします。

gentype half_cos (gentype x)

この関数は余弦(cosine)の計算をし、引数xの範囲は -216 ... +216 とします。

半精度浮動小数点関数を使うのは、深層学習が流行したということもありますが、ハードウェアがニーズに対応した側面もあります。半精度浮動小数点は一般に単精度や倍精度の演算よりも高速ですが、ハードが対応しない場合はパフォーマンスが向上するとは限りません。

注記

gentypeは「float, float2, float3, float4, float8, float16」のいずれかを指します。

nativeが窃盗にある関数はハードウェアの特殊演算ユニットに1対1または1対多のマッピングを行ないます。三角関数、指数関数、対数やべき乗の計算で高速な演算を行なうことができます。精度についてはOpenCLの実装によります。

gentype native_cos (gentype x)

余弦(cosine)の計算をしますが、引数xの範囲は実装システムに依存します。

10.4.4. 浮動小数点定数

浮動小数点境界値

注記

詳しくは「表:浮動小数点境界値」(表B.105「表:浮動小数点境界値」)を参照ください。

浮動小数点数マクロ

注記

詳しくは「表:浮動小数点マクロ」(表:浮動小数点マクロ)と「表:浮動小数点との互換性」(表B.106「表:浮動小数点との互換性」)と「表:浮動小数点定数」(表B.107「表:浮動小数点定数」)を参照ください。

10.4.5. 倍精度

注記

詳しくは「表:倍精度型マクロ」(表:倍精度型マクロ)と「表:倍精度型の互換性」(表B.108「表:倍精度型の互換性」)と「表:倍精度定数」(表B.109「表:倍精度定数」)を参照ください。

Copyright 2018-2019, by Masaki Komatsu