👌
★ "C# deligate" implement "java"
C#独自の概念であるdeligate処理をJavaにマイグレーションするとどうなるか?
deligateを理解するために、JavaへMigration
-
C#の"deligate"をJavaへMigrationする際、Javaには直接的な関数ポインタに相当する機能がないため、代わりに"Interface"や"ラムダ式"を活用することで、同等の動作を実現できる。
-
以下は、C#のdeligate処理コードをJavaに変換した例。C#のdeligateが提供する柔軟な関数参照と呼び出しのメカニズムを、Javaでも効果的に再現することができる。
/* [java] */
import java.util.ArrayList;
import java.util.List;
public class SampleEx702 {
// Actionインターフェースの定義
interface Action {
void execute(int a);
}
// 1つ目の処理
static void func1(int a) {
System.out.println("a=" + a);
}
// 2つ目の処理
static void func2(int a) {
System.out.println("a*2=" + (a * 2));
}
// 3つ目の処理
static void func3(int a) {
System.out.println("a*3=" + (a * 3));
}
public static void main(String[] args) {
// Actionのリストを作成
List<Action> actions = new ArrayList<>();
// 処理の追加
actions.add(SampleEx702::func1);
actions.add(SampleEx702::func2);
actions.add(SampleEx702::func3);
// 処理の実行
for (Action action : actions) {
action.execute(3);
}
}
}
/* [cs] */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx702
{
class Program
{
// デリゲートの宣言
delegate void Action(int a);
// 1つ目の処理
static void Func1(int a)
{
Console.WriteLine("a={0}",a);
}
// 2つ目の処理
static void Func2(int a)
{
Console.WriteLine("a*2={0}", a * 2);
}
// 3つ目の処理
static void Func3(int a)
{
Console.WriteLine("a*3={0}", a * 3);
}
static void Main(string[] args)
{
// デリゲートaの作成
Action a = new Action(Func1);
// 処理の追加
a += new Action(Func2);
a += new Action(Func3);
// 処理の実行
a(3);
}
}
}
a=3
a*2=6
a*3=9
並列処理版
/* [java] */
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SampleEx702 {
// Actionインターフェースの定義
interface Action {
void execute(int a);
}
// 1つ目の処理
static void func1(int a) {
System.out.println("a=" + a);
}
// 2つ目の処理
static void func2(int a) {
System.out.println("a*2=" + (a * 2));
}
// 3つ目の処理
static void func3(int a) {
System.out.println("a*3=" + (a * 3));
}
public static void main(String[] args) {
// Actionのリストを作成
List<Action> actions = new ArrayList<>();
// 処理の追加
actions.add(SampleEx702::func1);
actions.add(SampleEx702::func2);
actions.add(SampleEx702::func3);
// ExecutorServiceを作成
ExecutorService executor = Executors.newFixedThreadPool(actions.size());
// 処理の実行
for (Action action : actions) {
executor.execute(() -> action.execute(3));
}
// ExecutorServiceをシャットダウン
executor.shutdown();
}
}
/* [cs] */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx702
{
class Program
{
// デリゲートの宣言
delegate void Action(int a);
// 1つ目の処理
static void Func1(int a)
{
Console.WriteLine("a={0}", a);
}
// 2つ目の処理
static void Func2(int a)
{
Console.WriteLine("a*2={0}", a * 2);
}
// 3つ目の処理
static void Func3(int a)
{
Console.WriteLine("a*3={0}", a * 3);
}
static void Main(string[] args)
{
// デリゲートaの作成
Action a = new Action(Func1);
// 処理の追加
a += new Action(Func2);
a += new Action(Func3);
// 処理の実行を並列で行う
List<Task> tasks = new List<Task>();
foreach (Action action in a.GetInvocationList())
{
tasks.Add(Task.Run(() => action(3)));
}
// 全てのタスクが完了するのを待つ
Task.WaitAll(tasks.ToArray());
}
}
}
番外編:GPU使用
CLのkernel入れ替えを工夫した例。
- 関数型インターフェースの使用: Function<cl_mem, cl_kernel>を使って、カーネルを生成するための関数を引数として受け取る。
- カーネル生成メソッド: createKernelMultiplyByTwoとcreateKernelMultiplyByThreeメソッドを作成し、それぞれのカーネルを生成。
- 共通のカーネル作成メソッド: createKernelメソッドを作成し、カーネルのソースと名前を受け取ってカーネルを生成。
/* [java] Java 8以降*/
import org.jocl.*;
import java.util.function.Function;
// GPU処理のメソッド
static int gpuProcess(int a, Function<cl_mem, cl_kernel> kernelFunction) {
// OpenCLの初期化
CL.setExceptionsEnabled(true);
cl_platform_id[] platforms = new cl_platform_id[1];
CL.clGetPlatformIDs(1, platforms, null);
cl_platform_id platform = platforms[0];
cl_device_id[] devices = new cl_device_id[1];
CL.clGetDeviceIDs(platform, CL.CL_DEVICE_TYPE_GPU, 1, devices, null);
cl_device_id device = devices[0];
cl_context context = CL.clCreateContext(null, 1, new cl_device_id[]{device}, null, null, null);
cl_command_queue commandQueue = CL.clCreateCommandQueue(context, device, 0, null);
// バッファの作成
cl_mem memObjects = CL.clCreateBuffer(context, CL.CL_MEM_READ_WRITE | CL.CL_MEM_COPY_HOST_PTR, Sizeof.cl_int, new int[]{a}, null);
// カーネルの作成
cl_kernel kernel = kernelFunction.apply(memObjects);
// カーネル引数の設定
CL.clSetKernelArg(kernel, 0, Sizeof.cl_mem, Pointer.to(memObjects));
// カーネルの実行
long[] globalWorkSize = new long[]{1};
CL.clEnqueueNDRangeKernel(commandQueue, kernel, 1, null, globalWorkSize, null, 0, null, null);
// 結果の取得
int[] result = new int[1];
CL.clEnqueueReadBuffer(commandQueue, memObjects, CL.CL_TRUE, 0, Sizeof.cl_int, Pointer.to(result), 0, null, null);
// リソースの解放
CL.clReleaseMemObject(memObjects);
CL.clReleaseKernel(kernel);
CL.clReleaseCommandQueue(commandQueue);
CL.clReleaseContext(context);
return result[0];
}
// カーネルを生成するための関数
static cl_kernel createKernelMultiplyByTwo(cl_mem memObjects) {
String programSource = "__kernel void multiplyByTwo(__global int* a) { a[0] = a[0] * 2; }";
return createKernel(programSource, "multiplyByTwo", memObjects);
}
static cl_kernel createKernelMultiplyByThree(cl_mem memObjects) {
String programSource = "__kernel void multiplyByThree(__global int* a) { a[0] = a[0] * 3; }";
return createKernel(programSource, "multiplyByThree", memObjects);
}
// カーネルを作成する共通メソッド
static cl_kernel createKernel(String programSource, String kernelName, cl_mem memObjects) {
cl_context context = CL.clGetCurrentContext();
cl_program program = CL.clCreateProgramWithSource(context, 1, new String[]{programSource}, null, null);
CL.clBuildProgram(program, 0, null, null, null, null);
return CL.clCreateKernel(program, kernelName, null);
}
// 使用例
public static void main(String[] args) {
int result1 = gpuProcess(5, memObjects -> createKernelMultiplyByTwo(memObjects));
System.out.println("Result of multiplyByTwo: " + result1);
int result2 = gpuProcess(5, memObjects -> createKernelMultiplyByThree(memObjects));
System.out.println("Result of multiplyByThree: " + result2);
}
/* [cs] */
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using OpenCL.Net;
namespace SampleEx702
{
class Program
{
// デリゲートの宣言
delegate int Action(int a);
// 1つ目の処理
static int Func1(int a)
{
Console.WriteLine("CPU: a={0}", a);
return a;
}
// 2つ目の処理
static int Func2(int a)
{
Console.WriteLine("CPU: a*2={0}", a * 2);
return a * 2;
}
// 3つ目の処理
static int Func3(int a)
{
Console.WriteLine("CPU: a*3={0}", a * 3);
return a * 3;
}
// GPU処理のメソッド
static int GpuProcess(int a, string operation)
{
// OpenCLの初期化
ErrorCode error;
Cl.GetPlatformIDs(1, out cl_platform_id platform, out error);
Cl.GetDeviceIDs(platform, DeviceType.Gpu, 1, out cl_device_id device, out error);
cl_context context = Cl.CreateContext(null, 1, new[] { device }, null, IntPtr.Zero, out error);
cl_command_queue commandQueue = Cl.CreateCommandQueue(context, device, 0, out error);
// カーネルの作成
string programSource = operation switch
{
"Func1" => "__kernel void func1(__global int* a) { a[0] = a[0]; }",
"Func2" => "__kernel void func2(__global int* a) { a[0] = a[0] * 2; }",
"Func3" => "__kernel void func3(__global int* a) { a[0] = a[0] * 3; }",
_ => throw new ArgumentException("Invalid operation")
};
cl_program program = Cl.CreateProgramWithSource(context, 1, new[] { programSource }, null, out error);
Cl.BuildProgram(program, 0, null, null, null, IntPtr.Zero);
cl_kernel kernel = Cl.CreateKernel(program, operation, out error);
// バッファの作成
cl_mem memObjects = Cl.CreateBuffer(context, MemFlags.ReadWrite | MemFlags.CopyHostPtr, sizeof(int), new[] { a }, out error);
// カーネル引数の設定
Cl.SetKernelArg(kernel, 0, memObjects);
// カーネルの実行
IntPtr[] globalWorkSize = new IntPtr[] { new IntPtr(1) };
Cl.EnqueueNDRangeKernel(commandQueue, kernel, 1, null, globalWorkSize, null, 0, null, out error);
// 結果の取得
int[] result = new int[1];
Cl.EnqueueReadBuffer(commandQueue, memObjects, Bool.True, IntPtr.Zero, sizeof(int), result, 0, null, out error);
// リソースの解放
Cl.ReleaseMemObject(memObjects);
Cl.ReleaseKernel(kernel);
Cl.ReleaseProgram(program);
Cl.ReleaseCommandQueue(commandQueue);
Cl.ReleaseContext(context);
return result[0];
}
static void Main(string[] args)
{
// デリゲートの作成
Action actions = new Action(Func1);
actions += new Action(Func2);
actions += new Action(Func3);
// GPU処理の実行
List<Task<int>> gpuTasks = new List<Task<int>>();
foreach (Action action in actions.GetInvocationList())
{
string operationName = action.Method.Name; // メソッド名を取得
gpuTasks.Add(Task.Run(() => GpuProcess(3, operationName)));
}
// 全てのタスクが完了するのを待つ
Task.WaitAll(gpuTasks.ToArray());
// 結果の表示
foreach (var task in gpuTasks)
{
Console.WriteLine("GPU Result: {0}", task.Result);
}
}
}
}
Discussion