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