質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

OpenCL

OpenCLは、システム上にある多くの計算資源を統一して扱うためのAPIセット。CPU/GPU/DSP/FPGAなどの異種混在環境(ヘテロジニアス・システム)で並列処理を用いるプログラム作成のためのフレームワークです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

並列処理

複数の計算が同時に実行される手法

Q&A

解決済

1回答

2961閲覧

C#にてOpenCLのclSetKernelArg()の引数がうまく指定できない

yosse95ai

総合スコア39

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

OpenCL

OpenCLは、システム上にある多くの計算資源を統一して扱うためのAPIセット。CPU/GPU/DSP/FPGAなどの異種混在環境(ヘテロジニアス・システム)で並列処理を用いるプログラム作成のためのフレームワークです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

並列処理

複数の計算が同時に実行される手法

0グッド

0クリップ

投稿2021/01/11 05:59

編集2021/01/12 02:38

前提・実現したいこと

カットシステム様のC#によるOpenCL並列プログラミングという参考書で現在勉強をしています.

実行環境はVisual Studio 2019です.
本書はDLL呼び出しによりOpenCLを扱えるようにするものです.

「3-4 カーネルの引数にコンスタント」 という節でカーネルの引数でスカラ変数を扱えるようにします.

そこで, 本書ではclSetKernelArg(4)スカラ変数Bを用いる場面があります.

clSetKernelArgのC#側での実装は以下のようになっています.

C#

1//---------------------------------------------------------------------- 2//cl_int clSetKernelArg(cl_kernel kernel, 3// cl_uint arg_index, 4// size_t arg_size, 5// const void *arg_value ) 6[DllImport(clPath)] 7public static extern int clSetKernelArg( 8 IntPtr kernel, 9 uint arg_index, 10 IntPtr arg_size, 11 ref IntPtr arg_value 12  );

発生している問題・エラーメッセージ

参考書に書いてある通りに実装すると以下のようなエラーメッセージを吐かれます.

CS1620: 引数4はキーワード'ref'と共に渡す必要があります。

該当のソースコード

C#

1using System; 2using System.Runtime.InteropServices; 3 4#if true 5partial class Program 6{ 7 private static string clProc = "clProc"; 8 9 10 //---------------------------------------------------------------------- 11 // clMain 12 13 static void clMain(string[] src) 14 { 15 int status; 16 17 // initialize array 18 float B =2.2f; 19 float[] A = new float[100], C = new float[100]; 20 for (int i = 0; i < 100; i++) 21 A[i] = (float)(i + 100); 22 23 // get platform id 24 IntPtr[] platformId = new IntPtr[1]; 25 uint platformCount; 26 status = clGetPlatformIDs(1, platformId, out platformCount); 27 if (status != CL_SUCCESS) 28 throw new Exception("clGetPlatformIDs failed."); 29 30 // get device id 31 IntPtr[] deviceID = new IntPtr[1]; 32 uint deviceCount; 33 status = clGetDeviceIDs(platformId[0], 34 CL_DEVICE_TYPE_DEFAULT, 1, deviceID, out deviceCount); 35 if (status != CL_SUCCESS) 36 throw new Exception("clGetDeviceIDs failed."); 37 38 // create Context 39 int errcode_ret; 40 IntPtr context = clCreateContext(null, 1, deviceID, 41 null, IntPtr.Zero, out errcode_ret); 42 if (errcode_ret != CL_SUCCESS) 43 throw new Exception("clCreateContext failed."); 44 45 // create Command Queue 46 IntPtr queue = clCreateCommandQueue(context, 47 deviceID[0], 0, out errcode_ret); 48 if (errcode_ret != CL_SUCCESS) 49 throw new Exception("clCreateCommandQueue failed."); 50 51 // create program object 52 IntPtr prog = clCreateProgramWithSource(context, 53 (uint)(src.Length), src, null, out errcode_ret); 54 if (errcode_ret != CL_SUCCESS) 55 throw new Exception("clCreateProgramWithSource failed."); 56 57 // build program 58 status = clBuildProgram(prog, 1, deviceID, null, null, IntPtr.Zero); 59 if (status != CL_SUCCESS) 60 throw new Exception("clBuildProgram failed."); 61 62 //create kernel 63 IntPtr kernel = clCreateKernel(prog, clProc, out errcode_ret); 64 if (errcode_ret != CL_SUCCESS) 65 throw new Exception("clCreateKernel failed."); 66 67 // create memory object 68 GCHandle handle; 69 handle = GCHandle.Alloc(A, GCHandleType.Pinned); 70 IntPtr memA = clCreateBuffer(context, CL_MEM_READ_ONRY | CL_MEM_COPY_HOST_PTR, 71 (IntPtr)(Marshal.SizeOf(A[0]) * A.Length), handle.AddrOfPinnedObject(), 72 out errcode_ret); 73 handle.Free(); 74 if (errcode_ret != CL_SUCCESS) 75 throw new Exception("clCreateBuffer A failed."); 76 77 IntPtr memC = clCreateBuffer(context, CL_MEM_WRITE_ONRY, 78 (IntPtr)(Marshal.SizeOf(C[0]) * C.Length), IntPtr.Zero, out errcode_ret); 79 if (errcode_ret != CL_SUCCESS) 80 throw new Exception("clCreateBuffer C failed."); 81 82 83 84 // set kernel parameter 85 status = clSetKernelArg(kernel, 0, (IntPtr)(Marshal.SizeOf(typeof(IntPtr))), ref memA); 86 if (status != CL_SUCCESS) 87 throw new Exception("clSetKernelArg A failed."); 88 89/*********************************************************************************/ 90/**該当箇所************************************************************************/ 91/*********************************************************************************/ 92 93 handle = GCHandle.Alloc(B, GCHandleType.Pinned); 94 status = clSetKernelArg(kernel, 1, (IntPtr)(Marshal.SizeOf(B)), handle.AddrOfPinnedObject()); 95 handle.Free(); 96 if (status != CL_SUCCESS) 97 throw new Exception("clSetKernelArg B failed."); 98 99/*********************************************************************************/ 100/**ここまで************************************************************************/ 101/*********************************************************************************/ 102 status = clSetKernelArg(kernel, 2, (IntPtr)(Marshal.SizeOf(typeof(IntPtr))), ref memC); 103 if (status != CL_SUCCESS) 104 throw new Exception("clSetKernelArg C failed."); 105 106 107 // request execute kernel 108 IntPtr[] globalSize = { (IntPtr)C.Length }; 109 status = clEnqueueNDRangeKernel(queue, kernel, 1, null, 110 globalSize, null, 0, null, IntPtr.Zero); 111 if (status != CL_SUCCESS) 112 throw new Exception("clEnqueueTask failed."); 113 114 115 // obtain results 116 handle = GCHandle.Alloc(C, GCHandleType.Pinned); 117 status = clEnqueueReadBuffer(queue, memC, CL_TURE, (IntPtr)0, 118 (IntPtr)(Marshal.SizeOf(typeof(float)) * C.Length), handle.AddrOfPinnedObject(), 119 0, null, IntPtr.Zero); 120 121 handle.Free(); 122 if (status != CL_SUCCESS) 123 throw new Exception("clEnqueueReadBuffer failed."); 124 125 // list results 126 Console.WriteLine("(A <?> B = C)\n"); 127 for (int i = 0; i < 10; i++) 128 Console.WriteLine("{0:F4} <?> {1:F2} = {2:F2}", A[i], B, C[i]); 129 130 131 //release resources 132 clReleaseMemObject(memC); 133 clReleaseMemObject(memA); 134 clReleaseKernel(kernel); 135 clReleaseProgram(prog); 136 clReleaseCommandQueue(queue); 137 clReleaseContext(context); 138 139 } 140 141 //---------------------------------------------------------------------- 142 // Main 143 static void Main(string[] args) 144 { 145 try 146 { 147 // 引数チェック 148 if (args.Length != 1) 149 throw new Exception("引数に *.clを指定してください."); 150 string[] src = { System.IO.File.ReadAllText(args[0].ToString()) }; 151 152 clMain(src); 153 } 154 catch(Exception ex) 155 { 156 Console.WriteLine("{0}{1}", ex.ToString(), ex.StackTrace.ToString()); 157 } 158 } 159} 160 161#endif

カーネルのコードは以下の通りです.

add.cl

1__kernel void 2clProc( __global const float A[], 3 __const float B, 4 __global float C[]) 5{ 6 int i=get_global_id(0); 7 8 C[i]=A[i]+B; 9}

試したこと

試しにrefキーワードを渡してみると

CS1510: refまたはout値は、割り当て可能な変数でなければなりません。

という風になり, 正しい解釈なのかわかりませんが, 第4引数をいったんIntPtr型変数の中に入れてみました.
該当箇所のコードは以下の通りです.

C#

1 handle = GCHandle.Alloc(B, GCHandleType.Pinned); 2 IntPtr b = handle.AddrOfPinnedObject(); 3 status = clSetKernelArg(kernel, 1, (IntPtr)(Marshal.SizeOf(B)), ref b); 4 handle.Free();

これにより, 前述のようなエラーは消えましたが, 計算結果が正しく表示されませんでした.

>sample add.cl (A <?> B = C) 100.0000 <?> 2.20 = 100.00 101.0000 <?> 2.20 = 101.00 102.0000 <?> 2.20 = 102.00 103.0000 <?> 2.20 = 103.00 104.0000 <?> 2.20 = 104.00 105.0000 <?> 2.20 = 105.00 106.0000 <?> 2.20 = 106.00 107.0000 <?> 2.20 = 107.00 108.0000 <?> 2.20 = 108.00 109.0000 <?> 2.20 = 109.00

このように2.20を足し合わせていない結果が返っています.
このため, Bの値が0とされているような印象を受けます.

補足情報

もしかしたら私のタイプミスでたまたまうまく動いたとかだと困るので, 該当箇所のみ写真を添付させていただきます.
該当箇所写真

文章や説明が分かりにくいかと思いますがよろしくお願いします.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/01/12 02:24 編集

> 前述のようなエラーは消えましたが, 計算結果が正しく表示されませんでした. 結果も記載した方が良いのでは。 直接の回答ではないですが、配列の長さやカーネルのコードの引数の数を変えたりして、どこまできちんとパラメータが渡っているか地道にデバッグして検証するしかないのではないでしょうか。
yosse95ai

2021/01/12 02:40

ご指摘ありがとうございます. 追記させていただきました. 結果からカーネルの第2引数に値が渡っていないような印象を受けます.
guest

回答1

0

ベストアンサー

推測になりますが、Bのポインタの参照を渡しているので、Bの値が渡っていないと思われます。clSetKernelArgの第4パラメータをref IntPtrではなくIntPtrでDllImportを別に定義すれば渡せるかもしれません。

C#

1[DllImport(clPath)] 2public static extern int clSetKernelArg( 3 IntPtr kernel, 4 uint arg_index, 5 IntPtr arg_size, 6 ref IntPtr arg_value 7  ); 8[DllImport(clPath)] 9public static extern int clSetKernelArg( 10 IntPtr kernel, 11 uint arg_index, 12 IntPtr arg_size, 13 IntPtr arg_value 14  ); 15 16... 17 18status = clSetKernelArg(kernel, 1, (IntPtr)(Marshal.SizeOf(B)), b); 19

投稿2021/01/12 04:34

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yosse95ai

2021/01/12 06:05

ありがとうございます. 定義を増やしたところ期待通りの動きをしてくれました.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問