View Javadoc
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
17   * property retrieval, providing a higher-level interface for GPU-accelerated evolutionary algorithm
18   * implementations. This class handles the OpenCL buffer management and type conversions necessary
19   * 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   * <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.getDeviceInfoLongArray(deviceId, 
43   *     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
92  	 * {@code CL_DEVICE_TYPE_ALL} as the 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
126 	 * {@code CL_DEVICE_TYPE_ALL} 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
141 	 * string properties from 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
164 	 * per dimension, which 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
186 	 * work group size, global 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,
205 	 * maximum clock frequency, 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 }