前提・実現したいこと
カットシステム様の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とされているような印象を受けます.
補足情報
もしかしたら私のタイプミスでたまたまうまく動いたとかだと困るので, 該当箇所のみ写真を添付させていただきます.
文章や説明が分かりにくいかと思いますがよろしくお願いします.
回答1件
あなたの回答
tips
プレビュー