Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/jcl/src/openj9.gpu/share/classes/com/ibm/gpu/CUDAManager.java
12565 views
1
/*[INCLUDE-IF JAVA_SPEC_VERSION >= 8]*/
2
/*******************************************************************************
3
* Copyright (c) 2014, 2021 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
22
*******************************************************************************/
23
package com.ibm.gpu;
24
25
import java.io.IOException;
26
import java.io.InputStream;
27
import java.security.AccessController;
28
import java.security.PrivilegedAction;
29
import java.util.ArrayList;
30
import java.util.Arrays;
31
import java.util.BitSet;
32
import java.util.HashMap;
33
import java.util.Map;
34
import java.util.Map.Entry;
35
import java.util.Properties;
36
import java.util.TreeMap;
37
38
import com.ibm.cuda.Cuda;
39
import com.ibm.cuda.CudaDevice;
40
41
/*[IF Sidecar19-SE]*/
42
import java.lang.invoke.MethodHandles;
43
import java.lang.invoke.VarHandle;
44
/*[ELSE]
45
import sun.misc.Unsafe;
46
/*[ENDIF]*/
47
48
/**
49
* This class contains information important to IBM GPU enabled functions.
50
*/
51
/*[IF JAVA_SPEC_VERSION >= 17]*/
52
@SuppressWarnings("removal")
53
/*[ENDIF] JAVA_SPEC_VERSION >= 17 */
54
public final class CUDAManager {
55
56
private static final class Configuration {
57
58
private static final String DEFAULT_MODEL_NAME = "DEFAULT"; //$NON-NLS-1$
59
60
private static final int DEFAULT_THRESHOLD = 30000;
61
62
private static void loadProperties(Properties properties, String resourceName) throws IOException {
63
PrivilegedAction<InputStream> action = () -> CUDAManager.class.getResourceAsStream(resourceName);
64
65
try (InputStream input = AccessController.doPrivileged(action)) {
66
if (input != null) {
67
properties.load(input);
68
}
69
}
70
}
71
72
private static boolean startsWithIgnoreCase(String string, String prefix) {
73
int prefixLength = prefix.length();
74
75
if (string.length() >= prefixLength) {
76
return string.regionMatches(true, 0, prefix, 0, prefixLength);
77
}
78
79
return true;
80
}
81
82
private final CUDAManager manager;
83
84
/*
85
* Keyed by model name; then by type.
86
*/
87
private final Map<String, Map<Type, Integer>> thresholds;
88
89
Configuration(CUDAManager manager) {
90
super();
91
this.manager = manager;
92
this.thresholds = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
93
94
readThresholds();
95
}
96
97
boolean checkSortProperty(String name) {
98
String value = getProperty(name);
99
100
if (value != null) {
101
if (value.isEmpty() // <br/>
102
|| value.equalsIgnoreCase("all") //$NON-NLS-1$
103
|| value.equalsIgnoreCase("sort")) { //$NON-NLS-1$
104
return true;
105
}
106
107
manager.outputIfVerbose(String.format(
108
"Invalid value \"%s\" given on system property %s", //$NON-NLS-1$
109
value, name));
110
}
111
112
return false;
113
}
114
115
private int getDefaultThreshold(Type type) {
116
Map<Type, Integer> modelMap = thresholds.get(DEFAULT_MODEL_NAME);
117
118
if (modelMap != null) {
119
Integer threshold = modelMap.get(type);
120
121
if (threshold != null) {
122
return threshold.intValue();
123
}
124
}
125
126
return DEFAULT_THRESHOLD;
127
}
128
129
int getDoubleThreshold() {
130
return getDefaultThreshold(Type.DOUBLE);
131
}
132
133
int getDoubleThreshold(String modelName) {
134
return getThreshold(modelName, Type.DOUBLE);
135
}
136
137
int getFloatThreshold() {
138
return getDefaultThreshold(Type.FLOAT);
139
}
140
141
int getFloatThreshold(String modelName) {
142
return getThreshold(modelName, Type.FLOAT);
143
}
144
145
int getIntThreshold() {
146
return getDefaultThreshold(Type.INT);
147
}
148
149
int getIntThreshold(String modelName) {
150
return getThreshold(modelName, Type.INT);
151
}
152
153
int getLongThreshold() {
154
return getDefaultThreshold(Type.LONG);
155
}
156
157
int getLongThreshold(String modelName) {
158
return getThreshold(modelName, Type.LONG);
159
}
160
161
private int getThreshold(String modelName, Type type) {
162
Map<Type, Integer> modelMap = thresholds.get(modelName);
163
164
if (modelMap != null) {
165
Integer threshold = modelMap.get(type);
166
167
if (threshold != null) {
168
return threshold.intValue();
169
}
170
}
171
172
return getDefaultThreshold(type);
173
}
174
175
private void readThresholds() {
176
Properties properties = new Properties();
177
178
try {
179
loadProperties(properties, "ibm_gpu_thresholds.properties"); //$NON-NLS-1$
180
} catch (IOException e) {
181
manager.outputIfVerbose("Warning: couldn't load threshold properties file: " //$NON-NLS-1$
182
+ e.getLocalizedMessage());
183
}
184
185
for (Entry<Object, Object> property : properties.entrySet()) {
186
String propertyName = String.valueOf(property.getKey());
187
188
for (Type type : Type.values()) {
189
String prefix = type.propertyPrefix;
190
191
if (!startsWithIgnoreCase(propertyName, prefix)) {
192
continue;
193
}
194
195
String propertyValue = String.valueOf(property.getValue());
196
197
try {
198
int value = Integer.parseInt(propertyValue);
199
200
if (0 < value && value < Integer.MAX_VALUE) {
201
String model = propertyName.substring(prefix.length()).replace('_', ' ');
202
Map<Type, Integer> modelMap = thresholds.get(model);
203
204
if (modelMap == null) {
205
thresholds.put(model, modelMap = new HashMap<>());
206
}
207
208
modelMap.put(type, Integer.valueOf(value));
209
}
210
} catch (NumberFormatException e) {
211
manager.outputIfVerbose(String.format(
212
"Warning: ignoring non-numeric threshold: %s = %s", //$NON-NLS-1$
213
propertyName, propertyValue));
214
}
215
break;
216
}
217
}
218
}
219
220
}
221
222
private static final class Lock {
223
224
Lock() {
225
super();
226
}
227
228
}
229
230
private static final class Singleton {
231
232
static final CUDAManager INSTANCE = new CUDAManager();
233
234
}
235
236
private static enum Type {
237
238
DOUBLE, FLOAT, INT, LONG;
239
240
final String propertyPrefix;
241
242
Type() {
243
propertyPrefix = "com.ibm.gpu." + name() + "sortThreshold."; //$NON-NLS-1$ //$NON-NLS-2$
244
}
245
246
}
247
248
private static final Lock lock = new Lock();
249
250
/**
251
* Return a CUDAManager instance.
252
*
253
* @return a CUDAManager instance
254
* @throws GPUConfigurationException
255
* This exception is not actually thrown; use {@code instance()} instead.
256
* @throws SecurityException
257
* If a security manager exists and the calling thread does not
258
* have permission to access the CUDAManager instance.
259
* @deprecated Use {@code instance()} instead.
260
*/
261
@Deprecated
262
public static CUDAManager getInstance()
263
throws GPUConfigurationException, SecurityException {
264
return instance();
265
}
266
267
/**
268
* Return a CUDAManager instance.
269
*
270
* @return a CUDAManager instance
271
* @throws SecurityException
272
* If a security manager exists and the calling thread does not
273
* have permission to access the CUDAManager instance.
274
*/
275
public static CUDAManager instance() throws SecurityException {
276
@SuppressWarnings("removal")
277
SecurityManager security = System.getSecurityManager();
278
279
if (security != null) {
280
security.checkPermission(GPUPermission.Access);
281
}
282
283
return instanceInternal();
284
}
285
286
static CUDAManager instanceInternal() {
287
return Singleton.INSTANCE;
288
}
289
290
/**
291
* Get the header used to prefix all IBM GPU related output.
292
*
293
* @return The header used for IBM GPU related output.
294
*/
295
public static String getOutputHeader() {
296
return "[IBM GPU]:"; //$NON-NLS-1$
297
}
298
299
static String getProperty(String name) {
300
PrivilegedAction<String> action = () -> System.getProperty(name);
301
302
return AccessController.doPrivileged(action);
303
}
304
305
/**
306
* Get the version of this class.
307
*
308
* @return Returns the version of this class.
309
*/
310
public static String getVersion() {
311
return Version.VERSION;
312
}
313
314
/**
315
* Performs cleanup on the CUDAManager class.
316
*
317
* @deprecated This method has no effect; it will be removed in a future version.
318
*/
319
@Deprecated
320
public static void tearDown() {
321
return;
322
}
323
324
private final BitSet busyDevices;
325
326
private int defaultDeviceId;
327
328
private final int defaultDoubleThreshold;
329
330
private final int defaultFloatThreshold;
331
332
private final int defaultIntThreshold;
333
334
private final int defaultLongThreshold;
335
336
private CUDADevice[] devices;
337
338
/*[IF Sidecar19-SE]*/
339
private final VarHandle devicesHandle;
340
/*[ELSE]
341
private final long devicesOffset;
342
private final Unsafe unsafe;
343
/*[ENDIF]*/
344
345
private boolean doSortOnGPU;
346
347
private boolean enforceGPUSort;
348
349
private boolean verboseOutput;
350
351
CUDAManager() {
352
super();
353
busyDevices = new BitSet();
354
defaultDeviceId = 0;
355
devices = null;
356
doSortOnGPU = false;
357
enforceGPUSort = false;
358
359
// set this early for better feedback
360
verboseOutput = getProperty("com.ibm.gpu.verbose") != null; //$NON-NLS-1$
361
362
/*[IF Sidecar19-SE]*/
363
try {
364
devicesHandle = MethodHandles.lookup().findVarHandle(CUDAManager.class, "devices", CUDADevice[].class); //$NON-NLS-1$
365
} catch (IllegalAccessException | NoSuchFieldException e) {
366
throw new InternalError(e.toString(), e);
367
}
368
/*[ELSE]
369
try {
370
unsafe = Unsafe.getUnsafe();
371
devicesOffset = unsafe.objectFieldOffset(CUDAManager.class.getDeclaredField("devices")); //$NON-NLS-1$
372
} catch (NoSuchFieldException e) {
373
InternalError error = new InternalError(e.toString());
374
error.initCause(e);
375
throw error;
376
}
377
/*[ENDIF]*/
378
379
Configuration configuration = new Configuration(this);
380
381
defaultDoubleThreshold = configuration.getDoubleThreshold();
382
defaultFloatThreshold = configuration.getFloatThreshold();
383
defaultIntThreshold = configuration.getIntThreshold();
384
defaultLongThreshold = configuration.getLongThreshold();
385
386
if (configuration.checkSortProperty("com.ibm.gpu.enforce")) { //$NON-NLS-1$
387
doSortOnGPU = true;
388
enforceGPUSort = true;
389
} else if (configuration.checkSortProperty("com.ibm.gpu.enable")) { //$NON-NLS-1$
390
doSortOnGPU = true;
391
}
392
393
if (configuration.checkSortProperty("com.ibm.gpu.disable")) { //$NON-NLS-1$
394
doSortOnGPU = false;
395
enforceGPUSort = false;
396
}
397
}
398
399
/**
400
* Look for the next free device and mark it as busy.
401
*
402
* @return Returns the device ID of the next free device.
403
*/
404
public int acquireFreeDevice() {
405
synchronized (lock) {
406
int deviceId = busyDevices.nextClearBit(0);
407
408
if (deviceId < getDeviceCount()) {
409
outputIfVerbose("Acquired device: " + deviceId); //$NON-NLS-1$
410
busyDevices.set(deviceId);
411
} else {
412
outputIfVerbose("No available devices found"); //$NON-NLS-1$
413
deviceId = -1;
414
}
415
416
return deviceId;
417
}
418
}
419
420
private CUDADevice[] findDevices() {
421
int deviceCount = 0;
422
423
try {
424
deviceCount = Cuda.getDeviceCount();
425
} catch (Exception e) {
426
// Cuda.getDeviceCount() declares but never throws CudaException.
427
outputIfVerbose("Couldn't count devices due to: " + e.getLocalizedMessage()); //$NON-NLS-1$
428
} catch (NoClassDefFoundError e) {
429
outputIfVerbose("Unsupported platform detected"); //$NON-NLS-1$
430
}
431
432
CUDADevice[] allDevices = new CUDADevice[deviceCount];
433
434
if (deviceCount != 0) {
435
Configuration configuration = new Configuration(this);
436
437
for (int deviceId = 0; deviceId < deviceCount; ++deviceId) {
438
String modelName = ""; //$NON-NLS-1$
439
440
try {
441
modelName = new CudaDevice(deviceId).getName();
442
} catch (Exception e) {
443
// This is likely a CudaException but we can't catch it specifically
444
// or class loading verification would fail for this class.
445
outputIfVerbose("Warning: couldn't get the GPU model name for device " + deviceId); //$NON-NLS-1$
446
}
447
448
allDevices[deviceId] = new CUDADevice(deviceId, modelName, // <br/>
449
configuration.getDoubleThreshold(modelName), // <br/>
450
configuration.getFloatThreshold(modelName), // <br/>
451
configuration.getIntThreshold(modelName), // <br/>
452
configuration.getLongThreshold(modelName));
453
}
454
455
outputIfVerbose("Discovered " + deviceCount + " device(s)"); //$NON-NLS-1$ //$NON-NLS-2$
456
}
457
458
return allDevices;
459
}
460
461
/**
462
* Use this method to obtain a reference to an ArrayList containing
463
* references to all discovered CUDA devices.
464
*
465
* @return Returns an ArrayList containing all discovered CUDA
466
* devices - see {@link CUDADevice} for details.
467
*/
468
public ArrayList<CUDADevice> getCUDADevices() {
469
return new ArrayList<>(Arrays.asList(getDevices()));
470
}
471
472
/**
473
* Gets the ID of the default device, set to 0 by default.
474
*
475
* @return Returns the device ID of the current default device.
476
*/
477
public int getDefaultDevice() {
478
return defaultDeviceId;
479
}
480
481
/**
482
* Get a reference to the CUDA device by means of its index (with 0 being the first).
483
*
484
* @param deviceId The index of the device to retrieve (with 0 being the first).
485
* @return Returns a CUDA device at the specified index - see
486
* {@link CUDADevice} for details.
487
* @throws GPUConfigurationException Throws this exception if an invalid deviceId
488
* has been specified.
489
*/
490
public CUDADevice getDevice(int deviceId) throws GPUConfigurationException {
491
CUDADevice[] allDevices = getDevices();
492
493
if (0 <= deviceId && deviceId < allDevices.length) {
494
return allDevices[deviceId];
495
} else {
496
throw newGPUConfigurationException("Invalid device"); //$NON-NLS-1$
497
}
498
}
499
500
/**
501
* Identifies the number of available CUDA devices.
502
*
503
* @return Returns how many CUDA devices have been detected.
504
*/
505
public int getDeviceCount() {
506
return getDevices().length;
507
}
508
509
private CUDADevice[] getDevices() {
510
CUDADevice[] allDevices = devices;
511
512
if (allDevices == null) {
513
synchronized (lock) {
514
allDevices = devices;
515
516
if (allDevices == null) {
517
allDevices = findDevices();
518
/*[IF Sidecar19-SE]*/
519
devicesHandle.setRelease(this, allDevices);
520
/*[ELSE]
521
unsafe.putOrderedObject(this, devicesOffset, allDevices);
522
/*[ENDIF]*/
523
}
524
}
525
}
526
527
return allDevices;
528
}
529
530
/**
531
* Identifies the CUDA device that has the most memory available.
532
*
533
* @return Returns a reference to the CUDA device with the most memory available.
534
* @throws GPUConfigurationException Throws this exception if an
535
* attempt was made to access an invalid device (no longer available).
536
*/
537
public CUDADevice getDeviceWithMostAvailableMemory()
538
throws GPUConfigurationException {
539
CUDADevice[] allDevices = getDevices();
540
CUDADevice bestDevice = null;
541
int deviceCount = allDevices.length;
542
long mostFreeMem = 0;
543
544
for (int deviceId = 0; deviceId < deviceCount; ++deviceId) {
545
try {
546
long freeMem = new CudaDevice(deviceId).getFreeMemory();
547
548
if (mostFreeMem < freeMem || deviceId == 0) {
549
bestDevice = allDevices[deviceId];
550
mostFreeMem = freeMem;
551
}
552
} catch (Exception e) {
553
// ignore
554
}
555
}
556
557
return bestDevice;
558
}
559
560
/**
561
* Gets the minimum length of a double array that will be
562
* sorted using a GPU if enabled.
563
*
564
* @return The minimum length of a double array that will be
565
* sorted using a GPU.
566
*/
567
public int getDoubleThreshold() {
568
return defaultDoubleThreshold;
569
}
570
571
/**
572
* Use this method to return an array of enabled CUDA devices.
573
*
574
* @return Returns an array containing enabled CUDA devices -
575
* see {@link CUDADevice} for details.
576
*/
577
public CUDADevice[] getEnabledCUDADevices() {
578
return getDevices().clone();
579
}
580
581
/**
582
* Gets the minimum length of a float array that will be
583
* sorted using a GPU if enabled.
584
*
585
* @return The minimum length of a float array that will be
586
* sorted using a GPU.
587
*/
588
public int getFloatThreshold() {
589
return defaultFloatThreshold;
590
}
591
592
/**
593
* Get the amount of free memory (in bytes) available for the provided CUDA device.
594
* Does not persistently change the current device.
595
*
596
* @param deviceId The index of the device to query (with 0 being the first).
597
* @return Returns the amount of free memory available.
598
* @throws GPUConfigurationException Throw this exception if cannot get free memory amount.
599
*/
600
public long getFreeMemoryForDevice(int deviceId)
601
throws GPUConfigurationException {
602
if (0 <= deviceId && deviceId < getDeviceCount()) {
603
try {
604
CudaDevice device = new CudaDevice(deviceId);
605
606
return device.getFreeMemory();
607
} catch (Exception e) {
608
// This is likely a CudaException but we can't catch it specifically
609
// or class loading verification would fail for this class.
610
throw new GPUConfigurationException(e.getLocalizedMessage(), e);
611
}
612
} else {
613
throw newGPUConfigurationException("Invalid device"); //$NON-NLS-1$
614
}
615
}
616
617
/**
618
* Gets the minimum length of an int array that will be
619
* sorted using a GPU if enabled.
620
*
621
* @return The minimum length of an int array that will be
622
* sorted using a GPU.
623
*/
624
public int getIntThreshold() {
625
return defaultIntThreshold;
626
}
627
628
/**
629
* Gets the minimum length of a long array that will be
630
* sorted using a GPU if enabled.
631
*
632
* @return The minimum length of a long array that will be
633
* sorted using a GPU.
634
*/
635
public int getLongThreshold() {
636
return defaultLongThreshold;
637
}
638
639
/**
640
* Returns the next CUDA device that is available to run calculations on.
641
*
642
* @return -1 if there are no free devices, otherwise returns
643
* the ID of the free CUDA device.
644
*/
645
public int getNextAvailableDevice() {
646
synchronized (lock) {
647
int deviceId = busyDevices.nextClearBit(0);
648
649
if (deviceId < getDeviceCount()) {
650
outputIfVerbose("Device " + deviceId + " was free"); //$NON-NLS-1$ //$NON-NLS-2$
651
} else {
652
outputIfVerbose("No free devices!"); //$NON-NLS-1$
653
deviceId = -1;
654
}
655
656
return deviceId;
657
}
658
}
659
660
/**
661
* Get the value of the verboseGPUOutput flag.
662
*
663
* @return Whether or not verbose output should be produced.
664
*/
665
public boolean getVerboseGPUOutput() {
666
return verboseOutput;
667
}
668
669
/**
670
* Use this method to identify if CUDA is supported on this machine and
671
* within this environment: returns true if the number of CUDA devices
672
* detected is greater than 0.
673
*
674
* @return Returns true if one or more CUDA devices have been detected.
675
*/
676
public boolean hasCUDASupport() {
677
return getDeviceCount() != 0;
678
}
679
680
/**
681
* This method provides a means to determine if sort is
682
* enabled to be used by any available CUDA device.
683
*
684
* @return Returns true if GPU sort is enabled.
685
*/
686
public boolean isSortEnabledOnGPU() {
687
return doSortOnGPU;
688
}
689
690
/**
691
* This method provides a means to determine if sort is
692
* forced to be used by any available CUDA device.
693
*
694
* @return Returns true if GPU sort is forced.
695
*/
696
public boolean isSortEnforcedOnGPU() {
697
return enforceGPUSort;
698
}
699
700
private GPUConfigurationException newGPUConfigurationException(String message) {
701
outputIfVerbose(message);
702
return new GPUConfigurationException(getOutputHeader() + ' ' + message);
703
}
704
705
void outputIfVerbose(String message) {
706
if (verboseOutput) {
707
System.out.printf(
708
"%s [time.ms=%d]: %s\n", //$NON-NLS-1$
709
getOutputHeader(),
710
Long.valueOf(System.currentTimeMillis()), message);
711
}
712
}
713
714
/**
715
* Print information for each detected CUDA device.
716
*/
717
public void printAllDeviceInfo() {
718
CUDADevice[] allDevices = getDevices();
719
720
System.out.println("Number of devices: " + allDevices.length); //$NON-NLS-1$
721
722
for (CUDADevice device : allDevices) {
723
System.out.println(device);
724
}
725
}
726
727
/**
728
* Mark a device as being free; must be in a try finally block as we MUST
729
* release the handle regardless of whether or not a sort was successful.
730
*
731
* @param deviceId The device to be marked as free.
732
*/
733
public void releaseDevice(int deviceId) {
734
synchronized (lock) {
735
if (0 <= deviceId && deviceId < getDeviceCount()) {
736
busyDevices.clear(deviceId);
737
outputIfVerbose("Released device: " + deviceId); //$NON-NLS-1$
738
}
739
}
740
}
741
742
/**
743
* Sets the default device to the given device ID.
744
* @param deviceId The new default device.
745
*/
746
public void setDefaultDevice(int deviceId) {
747
defaultDeviceId = deviceId;
748
}
749
750
/**
751
* Use this method to set the device to use for subsequent calls.
752
*
753
* @param deviceId Set the default device ID to be this.
754
* @throws GPUConfigurationException Throws this exception if an invalid device
755
* number was specified.
756
*/
757
public void setDevice(int deviceId) throws GPUConfigurationException {
758
if (0 <= deviceId && deviceId < getDeviceCount()) {
759
this.setDefaultDevice(deviceId);
760
} else {
761
throw newGPUConfigurationException("Invalid device"); //$NON-NLS-1$
762
}
763
}
764
765
/**
766
* Set the value of the verboseGPUOutput flag. When this flag is true, GPU
767
* output will be produced.
768
*
769
* @param condition Whether or not verbose output should be produced.
770
*/
771
public void setVerboseGPU(boolean condition) {
772
verboseOutput = condition;
773
}
774
775
}
776
777