View Javadoc
1   package net.bmahe.genetics4j.gpu.opencl;
2   
3   import java.util.Arrays;
4   import java.util.List;
5   import java.util.Objects;
6   
7   import org.apache.commons.lang3.Validate;
8   import org.jocl.CL;
9   import org.jocl.Pointer;
10  import org.jocl.Sizeof;
11  import org.jocl.cl_device_id;
12  import org.jocl.cl_platform_id;
13  
14  /**
15   * Utility class providing convenient methods for OpenCL device discovery and information queries.
16   * 
17   * <p>DeviceUtils encapsulates the low-level OpenCL API calls required for device enumeration and property retrieval,
18   * providing a higher-level interface for GPU-accelerated evolutionary algorithm implementations. This class handles the
19   * OpenCL buffer management and type conversions necessary for interacting with the native OpenCL runtime.
20   * 
21   * <p>Key functionality includes:
22   * <ul>
23   * <li><strong>Device enumeration</strong>: Discover available devices on OpenCL platforms</li>
24   * <li><strong>Property queries</strong>: Retrieve device characteristics and capabilities</li>
25   * <li><strong>Type conversions</strong>: Convert between OpenCL native types and Java types</li>
26   * <li><strong>Buffer management</strong>: Handle memory allocation for OpenCL information queries</li>
27   * </ul>
28   * 
29   * <p>Common usage patterns:
30   * 
31   * <pre>{@code
32   * // Enumerate devices on a platform
33   * int deviceCount = DeviceUtils.numDevices(platformId);
34   * List<cl_device_id> deviceIds = DeviceUtils.getDeviceIds(platformId, deviceCount);
35   * 
36   * // Query device properties
37   * String deviceName = DeviceUtils.getDeviceInfoString(deviceId, CL.CL_DEVICE_NAME);
38   * int computeUnits = DeviceUtils.getDeviceInfoInt(deviceId, CL.CL_DEVICE_MAX_COMPUTE_UNITS);
39   * long maxWorkGroupSize = DeviceUtils.getDeviceInfoLong(deviceId, CL.CL_DEVICE_MAX_WORK_GROUP_SIZE);
40   * 
41   * // Query array properties
42   * int maxDimensions = DeviceUtils.getDeviceInfoInt(deviceId, CL.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
43   * long[] maxWorkItemSizes = DeviceUtils
44   * 		.getDeviceInfoLongArray(deviceId, CL.CL_DEVICE_MAX_WORK_ITEM_SIZES, maxDimensions);
45   * }</pre>
46   * 
47   * <p>Device type filtering:
48   * <ul>
49   * <li><strong>CL_DEVICE_TYPE_ALL</strong>: All available devices</li>
50   * <li><strong>CL_DEVICE_TYPE_GPU</strong>: GPU devices only</li>
51   * <li><strong>CL_DEVICE_TYPE_CPU</strong>: CPU devices only</li>
52   * <li><strong>CL_DEVICE_TYPE_ACCELERATOR</strong>: Accelerator devices only</li>
53   * </ul>
54   * 
55   * <p>Error handling:
56   * <ul>
57   * <li><strong>Parameter validation</strong>: Validates all input parameters</li>
58   * <li><strong>OpenCL error propagation</strong>: OpenCL errors are propagated as runtime exceptions</li>
59   * <li><strong>Memory management</strong>: Automatically handles buffer allocation and cleanup</li>
60   * </ul>
61   * 
62   * @see Device
63   * @see DeviceReader
64   * @see net.bmahe.genetics4j.gpu.opencl.model.DeviceType
65   */
66  public class DeviceUtils {
67  
68  	private DeviceUtils() {
69  
70  	}
71  
72  	/**
73  	 * Returns the number of OpenCL devices of the specified type available on the platform.
74  	 * 
75  	 * @param platformId the OpenCL platform to query
76  	 * @param deviceType the type of devices to count (e.g., CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ALL)
77  	 * @return the number of available devices of the specified type
78  	 * @throws IllegalArgumentException if platformId is null
79  	 */
80  	public static int numDevices(final cl_platform_id platformId, final long deviceType) {
81  		Objects.requireNonNull(platformId);
82  
83  		final int[] numDevices = new int[1];
84  		CL.clGetDeviceIDs(platformId, deviceType, 0, null, numDevices);
85  
86  		return numDevices[0];
87  	}
88  
89  	/**
90  	 * Returns the total number of OpenCL devices available on the platform.
91  	 * 
92  	 * <p>This is equivalent to calling {@link #numDevices(cl_platform_id, long)} with {@code CL_DEVICE_TYPE_ALL} as the
93  	 * device type.
94  	 * 
95  	 * @param platformId the OpenCL platform to query
96  	 * @return the total number of available devices on the platform
97  	 * @throws IllegalArgumentException if platformId is null
98  	 */
99  	public static int numDevices(final cl_platform_id platformId) {
100 		return numDevices(platformId, CL.CL_DEVICE_TYPE_ALL);
101 	}
102 
103 	/**
104 	 * Returns a list of OpenCL device identifiers of the specified type from the platform.
105 	 * 
106 	 * @param platformId the OpenCL platform to query
107 	 * @param numDevices the number of devices to retrieve
108 	 * @param deviceType the type of devices to retrieve (e.g., CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ALL)
109 	 * @return list of OpenCL device identifiers
110 	 * @throws IllegalArgumentException if platformId is null or numDevices is not positive
111 	 */
112 	public static List<cl_device_id> getDeviceIds(final cl_platform_id platformId, final int numDevices,
113 			final long deviceType) {
114 		Objects.requireNonNull(platformId);
115 		Validate.isTrue(numDevices > 0);
116 
117 		cl_device_id[] deviceIds = new cl_device_id[numDevices];
118 		CL.clGetDeviceIDs(platformId, deviceType, numDevices, deviceIds, null);
119 
120 		return Arrays.asList(deviceIds);
121 	}
122 
123 	/**
124 	 * Returns a list of all OpenCL device identifiers from the platform.
125 	 * 
126 	 * <p>This is equivalent to calling {@link #getDeviceIds(cl_platform_id, int, long)} with {@code CL_DEVICE_TYPE_ALL}
127 	 * as the device type.
128 	 * 
129 	 * @param platformId the OpenCL platform to query
130 	 * @param numDevices the number of devices to retrieve
131 	 * @return list of all OpenCL device identifiers
132 	 * @throws IllegalArgumentException if platformId is null or numDevices is not positive
133 	 */
134 	public static List<cl_device_id> getDeviceIds(final cl_platform_id platformId, final int numDevices) {
135 		return getDeviceIds(platformId, numDevices, CL.CL_DEVICE_TYPE_ALL);
136 	}
137 
138 	/**
139 	 * Queries and returns a string property of the specified OpenCL device.
140 	 * 
141 	 * <p>This method handles the OpenCL API calls and buffer management required to retrieve string properties from
142 	 * devices, such as device name, vendor, or version information.
143 	 * 
144 	 * @param deviceId  the OpenCL device to query
145 	 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_NAME, CL_DEVICE_VENDOR)
146 	 * @return the string value of the requested device property
147 	 * @throws IllegalArgumentException if deviceId is null
148 	 */
149 	public static String getDeviceInfoString(final cl_device_id deviceId, final int parameter) {
150 		Objects.requireNonNull(deviceId);
151 
152 		final long[] size = new long[1];
153 		CL.clGetDeviceInfo(deviceId, parameter, 0, null, size);
154 
155 		final byte[] buffer = new byte[(int) size[0]];
156 		CL.clGetDeviceInfo(deviceId, parameter, buffer.length, Pointer.to(buffer), null);
157 
158 		return new String(buffer, 0, buffer.length - 1);
159 	}
160 
161 	/**
162 	 * Queries and returns a long array property of the specified OpenCL device.
163 	 * 
164 	 * <p>This method is useful for retrieving array properties such as maximum work-item sizes per dimension, which
165 	 * require multiple values to fully describe the device capability.
166 	 * 
167 	 * @param deviceId  the OpenCL device to query
168 	 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_WORK_ITEM_SIZES)
169 	 * @param size      the number of long values to retrieve
170 	 * @return array of long values for the requested device property
171 	 * @throws IllegalArgumentException if deviceId is null or size is not positive
172 	 */
173 	public static long[] getDeviceInfoLongArray(final cl_device_id deviceId, final int parameter, final int size) {
174 		Objects.requireNonNull(deviceId);
175 		Validate.isTrue(size > 0);
176 
177 		final long[] values = new long[size];
178 		CL.clGetDeviceInfo(deviceId, parameter, Sizeof.cl_long * size, Pointer.to(values), null);
179 
180 		return values;
181 	}
182 
183 	/**
184 	 * Queries and returns a single long property of the specified OpenCL device.
185 	 * 
186 	 * <p>This method is useful for retrieving single long value properties such as maximum work group size, global
187 	 * memory size, or local memory size.
188 	 * 
189 	 * @param deviceId  the OpenCL device to query
190 	 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_WORK_GROUP_SIZE)
191 	 * @return the long value of the requested device property
192 	 * @throws IllegalArgumentException if deviceId is null
193 	 */
194 	public static long getDeviceInfoLong(final cl_device_id deviceId, final int parameter) {
195 		Objects.requireNonNull(deviceId);
196 
197 		final long[] values = getDeviceInfoLongArray(deviceId, parameter, 1);
198 
199 		return values[0];
200 	}
201 
202 	/**
203 	 * Queries and returns a single integer property of the specified OpenCL device.
204 	 * 
205 	 * <p>This method is useful for retrieving integer properties such as maximum compute units, maximum clock frequency,
206 	 * or maximum work-item dimensions.
207 	 * 
208 	 * @param deviceId  the OpenCL device to query
209 	 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_COMPUTE_UNITS)
210 	 * @return the integer value of the requested device property
211 	 * @throws IllegalArgumentException if deviceId is null
212 	 */
213 	public static int getDeviceInfoInt(final cl_device_id deviceId, final int parameter) {
214 		Objects.requireNonNull(deviceId);
215 
216 		final int[] values = new int[1];
217 		CL.clGetDeviceInfo(deviceId, parameter, Sizeof.cl_int, Pointer.to(values), null);
218 
219 		return values[0];
220 	}
221 }