Add openCL support

This commit is contained in:
Laurent Clouet
2018-08-24 19:46:03 +02:00
parent cde559c215
commit f81d3b67b3
17 changed files with 479 additions and 129 deletions

View File

@@ -19,120 +19,31 @@
package com.sheepit.client.hardware.gpu;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.sheepit.client.Configuration;
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
import com.sheepit.client.os.OS;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import com.sheepit.client.os.Windows;
public class GPU {
public static List<GPUDevice> devices = null;
public static boolean generate() {
devices = new LinkedList<GPUDevice>();
List<GPUDevice> gpus = new Nvidia().getGpus();
if (gpus != null) {
devices.addAll(gpus);
}
OS os = OS.getOS();
String path = os.getCUDALib();
if (path == null) {
System.out.println("GPU::generate no CUDA lib path found");
return false;
}
CUDA cudalib = null;
try {
cudalib = (CUDA) Native.loadLibrary(path, CUDA.class);
}
catch (java.lang.UnsatisfiedLinkError e) {
System.out.println("GPU::generate failed to load CUDA lib (path: " + path + ")");
return false;
}
catch (java.lang.ExceptionInInitializerError e) {
System.out.println("GPU::generate ExceptionInInitializerError " + e);
return false;
}
catch (Exception e) {
System.out.println("GPU::generate generic exception " + e);
return false;
}
int result = CUresult.CUDA_ERROR_UNKNOWN;
result = cudalib.cuInit(0);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuInit failed (ret: " + result + ")");
if (result == CUresult.CUDA_ERROR_UNKNOWN) {
System.out.println("If you are running Linux, this error is usually due to nvidia kernel module 'nvidia_uvm' not loaded.");
System.out.println("Relaunch the application as root or load the module.");
System.out.println("Most of time it does fix the issue.");
if (os instanceof Windows) { // opencl detection will crash on Mac (and sometimes on Linux)
gpus = new OpenCL().getGpus();
if (gpus != null) {
devices.addAll(gpus);
}
return false;
}
if (result == CUresult.CUDA_ERROR_NO_DEVICE) {
return false;
}
IntByReference count = new IntByReference();
result = cudalib.cuDeviceGetCount(count);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuDeviceGetCount failed (ret: " + CUresult.stringFor(result) + ")");
return false;
}
HashMap<Integer, GPUDevice> devicesWithPciId = new HashMap<Integer, GPUDevice>(count.getValue());
for (int num = 0; num < count.getValue(); num++) {
IntByReference aDevice = new IntByReference();
result = cudalib.cuDeviceGet(aDevice, num);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuDeviceGet failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
IntByReference pciBusId = new IntByReference();
result = cudalib.cuDeviceGetAttribute(pciBusId, CUDeviceAttribute.CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, aDevice.getValue());
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuDeviceGetAttribute for CU_DEVICE_ATTRIBUTE_PCI_BUS_ID failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
byte name[] = new byte[256];
result = cudalib.cuDeviceGetName(name, 256, num);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuDeviceGetName failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
LongByReference ram = new LongByReference();
try {
result = cudalib.cuDeviceTotalMem_v2(ram, num);
}
catch (UnsatisfiedLinkError e) {
// fall back to old function
result = cudalib.cuDeviceTotalMem(ram, num);
}
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("GPU::generate cuDeviceTotalMem failed (ret: " + CUresult.stringFor(result) + ")");
return false;
}
devicesWithPciId.put(pciBusId.getValue(), new GPUDevice(new String(name).trim(), ram.getValue(), "FAKE"));
}
// generate proper cuda id
// in theory a set to environment "CUDA_DEVICE_ORDER=PCI_BUS_ID" should be enough but it didn't work
int i = 0;
for (Map.Entry<Integer, GPUDevice> entry : devicesWithPciId.entrySet()){
GPUDevice aDevice = entry.getValue();
aDevice.setCudaName("CUDA_" + Integer.toString(i));
devices.add(aDevice);
i++;
}
return true;
@@ -150,9 +61,14 @@ public class GPU {
return devs;
}
public static List<GPUDevice> listDevices() {
public static List<GPUDevice> listDevices(Configuration config) {
if (devices == null) {
generate();
if (config.getDetectGPUs()) {
generate();
}
else {
devices = new LinkedList<GPUDevice>();
}
}
return devices;
@@ -172,7 +88,7 @@ public class GPU {
}
for (GPUDevice dev : devices) {
if (device_model.equals(dev.getCudaName()) || device_model.equals(dev.getModel())) {
if (device_model.equals(dev.getId()) || device_model.equals(dev.getModel())) {
return dev;
}
}

View File

@@ -20,15 +20,25 @@
package com.sheepit.client.hardware.gpu;
public class GPUDevice {
private String type;
private String model;
private long memory; // in B
private String cudaName;
private String id;
public GPUDevice(String model, long ram, String cuda) {
public GPUDevice(String type, String model, long ram, String id) {
this.type = type;
this.model = model;
this.memory = ram;
this.cudaName = cuda;
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getModel() {
@@ -47,17 +57,17 @@ public class GPUDevice {
this.memory = memory;
}
public String getCudaName() {
return cudaName;
public String getId() {
return id;
}
public void setCudaName(String cudaName) {
this.cudaName = cudaName;
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "GPUDevice [model=" + model + ", memory=" + memory + ", cudaName=" + cudaName + "]";
return "GPUDevice [type=" + type + ", model='" + model + "', memory=" + memory + ", id=" + id + "]";
}
public int getRecommandedTileSize() {

View File

@@ -0,0 +1,7 @@
package com.sheepit.client.hardware.gpu;
import java.util.List;
public interface GPULister {
public abstract List<GPUDevice> getGpus();
}

View File

@@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client.hardware.gpu;
package com.sheepit.client.hardware.gpu.nvidia;
import com.sun.jna.Library;
import com.sun.jna.ptr.IntByReference;

View File

@@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client.hardware.gpu;
package com.sheepit.client.hardware.gpu.nvidia;
/**
* CUDA Device properties. Taken directly from the online manual:

View File

@@ -26,7 +26,7 @@
*/
//package jcuda.driver;
package com.sheepit.client.hardware.gpu;
package com.sheepit.client.hardware.gpu.nvidia;
/**
* Error codes.<br />

View File

@@ -0,0 +1,123 @@
package com.sheepit.client.hardware.gpu.nvidia;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.sheepit.client.hardware.gpu.nvidia.CUDeviceAttribute;
import com.sheepit.client.hardware.gpu.GPUDevice;
import com.sheepit.client.hardware.gpu.GPULister;
import com.sheepit.client.os.OS;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
public class Nvidia implements GPULister {
public static String TYPE = "CUDA";
@Override
public List<GPUDevice> getGpus() {
OS os = OS.getOS();
String path = os.getCUDALib();
if (path == null) {
return null;
}
CUDA cudalib = null;
try {
cudalib = (CUDA) Native.loadLibrary(path, CUDA.class);
}
catch (java.lang.UnsatisfiedLinkError e) {
return null;
}
catch (java.lang.ExceptionInInitializerError e) {
System.out.println("Nvidia::getGpus ExceptionInInitializerError " + e);
return null;
}
catch (Exception e) {
System.out.println("Nvidia::getGpus generic exception " + e);
return null;
}
int result = CUresult.CUDA_ERROR_UNKNOWN;
result = cudalib.cuInit(0);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuInit failed (ret: " + result + ")");
if (result == CUresult.CUDA_ERROR_UNKNOWN) {
System.out.println("If you are running Linux, this error is usually due to nvidia kernel module 'nvidia_uvm' not loaded.");
System.out.println("Relaunch the application as root or load the module.");
System.out.println("Most of time it does fix the issue.");
}
return null;
}
if (result == CUresult.CUDA_ERROR_NO_DEVICE) {
return null;
}
IntByReference count = new IntByReference();
result = cudalib.cuDeviceGetCount(count);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuDeviceGetCount failed (ret: " + CUresult.stringFor(result) + ")");
return null;
}
List<GPUDevice> devices = new LinkedList<GPUDevice>();
HashMap<Integer, GPUDevice> devicesWithPciId = new HashMap<Integer, GPUDevice>(count.getValue());
for (int num = 0; num < count.getValue(); num++) {
IntByReference aDevice = new IntByReference();
result = cudalib.cuDeviceGet(aDevice, num);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuDeviceGet failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
IntByReference pciBusId = new IntByReference();
result = cudalib.cuDeviceGetAttribute(pciBusId, CUDeviceAttribute.CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, aDevice.getValue());
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuDeviceGetAttribute for CU_DEVICE_ATTRIBUTE_PCI_BUS_ID failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
byte name[] = new byte[256];
result = cudalib.cuDeviceGetName(name, 256, num);
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuDeviceGetName failed (ret: " + CUresult.stringFor(result) + ")");
continue;
}
LongByReference ram = new LongByReference();
try {
result = cudalib.cuDeviceTotalMem_v2(ram, num);
}
catch (UnsatisfiedLinkError e) {
// fall back to old function
result = cudalib.cuDeviceTotalMem(ram, num);
}
if (result != CUresult.CUDA_SUCCESS) {
System.out.println("Nvidia::getGpus cuDeviceTotalMem failed (ret: " + CUresult.stringFor(result) + ")");
return null;
}
devicesWithPciId.put(pciBusId.getValue(), new GPUDevice(TYPE, new String(name).trim(), ram.getValue(), "FAKE"));
}
// generate proper cuda id
// in theory a set to environment "CUDA_DEVICE_ORDER=PCI_BUS_ID" should be enough but it didn't work
int i = 0;
for (Map.Entry<Integer, GPUDevice> entry : devicesWithPciId.entrySet()){
GPUDevice aDevice = entry.getValue();
aDevice.setId(TYPE + "_" + Integer.toString(i));
devices.add(aDevice);
i++;
}
return devices;
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2013-2014 Laurent CLOUET
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client.hardware.gpu.opencl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import com.sheepit.client.hardware.gpu.GPUDevice;
import com.sheepit.client.hardware.gpu.GPULister;
import com.sheepit.client.hardware.gpu.opencl.OpenCLLib.CLDeviceId;
import com.sheepit.client.hardware.gpu.opencl.OpenCLLib.CLPlatformId;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
public class OpenCL implements GPULister {
public static String TYPE = "OPENCL";
@Override
public List<GPUDevice> getGpus() {
OpenCLLib lib = null;
String path = "OpenCL";
try {
lib = (OpenCLLib) Native.loadLibrary(path, OpenCLLib.class);
}
catch (java.lang.UnsatisfiedLinkError e) {
System.out.println("OpenCL::getGpus failed(A) to load OpenCL lib (path: " + path + ")");
return null;
}
catch (java.lang.ExceptionInInitializerError e) {
System.out.println("OpenCL::getGpus failed(B) ExceptionInInitializerError " + e);
return null;
}
catch (Exception e) {
System.out.println("OpenCL::getGpus failed(C) generic exception " + e);
return null;
}
int status = -1;
// get the number of platform
IntByReference number_platforms = new IntByReference();
status = lib.clGetPlatformIDs(0, null, number_platforms);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getGpus failed(D) status: " + status);
return null;
}
// now we can create the platforms
final OpenCLLib.CLPlatformId.ByReference e6ref = new OpenCLLib.CLPlatformId.ByReference();
OpenCLLib.CLPlatformId.ByReference[] plateforms = (OpenCLLib.CLPlatformId.ByReference[]) e6ref.toArray(number_platforms.getValue());
status = lib.clGetPlatformIDs(number_platforms.getValue(), plateforms, null);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getGpus failed(E) status: " + status);
return null;
}
List<GPUDevice> available_devices = new ArrayList<GPUDevice>(1);
// Devices are numbered consecutively across platforms.
int id = 0;
for (int i = 0; i < number_platforms.getValue(); i++) {
// get number of devices in platform
IntByReference device_count = new IntByReference();
status = lib.clGetDeviceIDs(plateforms[i], OpenCLLib.CL_DEVICE_TYPE_GPU, 0, null, device_count);
if (status == OpenCLLib.CL_DEVICE_NOT_FOUND) {
System.out.println("OpenCL::getGpus no device found on plateforms[" + i + "]");
continue;
}
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getGpus failed(F) status: " + status);
return null;
}
final OpenCLLib.CLDeviceId.ByReference e6ref4 = new OpenCLLib.CLDeviceId.ByReference();
OpenCLLib.CLDeviceId.ByReference[] devices = (OpenCLLib.CLDeviceId.ByReference[]) e6ref4.toArray(device_count.getValue());
status = lib.clGetDeviceIDs(plateforms[i], OpenCLLib.CL_DEVICE_TYPE_GPU, device_count.getValue(), devices, null);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getGpus failed(G) status: " + status);
return null;
}
for (int j = 0; j < device_count.getValue(); j++) {
String platform_vendor = getInfoPlatform(lib, plateforms[i], OpenCLLib.CL_PLATFORM_VENDOR);
if (platform_vendor != null && platform_vendor.toLowerCase().equals("advanced micro devices, inc.")) { // opencl is only used for amd gpus
String name = getInfodeviceString(lib, devices[j], OpenCLLib.CL_DEVICE_BOARD_NAME_AMD);
long vram = getInfodeviceLong(lib, devices[j], OpenCLLib.CL_DEVICE_GLOBAL_MEM_SIZE);
if (name != null && vram > 0) {
available_devices.add(new GPUDevice(TYPE, name, vram, TYPE + "_" + id));
}
}
id++;
}
}
return available_devices;
}
private static String getInfodeviceString(OpenCLLib lib, CLDeviceId.ByReference device, int type) {
byte name[] = new byte[256];
int status = lib.clGetDeviceInfo(device, type, 256, name, null);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getInfodeviceString failed(H) status: " + status + " type: " + type);
return null;
}
return new String(name).trim();
}
private static long getInfodeviceLong(OpenCLLib lib, CLDeviceId.ByReference device, int type) {
byte name[] = new byte[256];
int status = lib.clGetDeviceInfo(device, type, 256, name, null);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("OpenCL::getInfodeviceLong failed(I) status: " + status + " type: " + type);
return -1;
}
ByteBuffer wrapped = ByteBuffer.wrap(name);
wrapped.order(ByteOrder.LITTLE_ENDIAN);
return wrapped.getLong();
}
private static String getInfoPlatform(OpenCLLib lib, CLPlatformId.ByReference platform, int type) {
byte name[] = new byte[256];
int status = lib.clGetPlatformInfo(platform, type, 256, name, null);
if (status != OpenCLLib.CL_SUCCESS) {
System.out.println("GPU::getInfoPlatform failed(J) status: " + status + " type: " + type);
return null;
}
return new String(name).trim();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2013-2014 Laurent CLOUET
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client.hardware.gpu.opencl;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
public interface OpenCLLib extends Library {
// status
public static final int CL_SUCCESS = 0;
public static final int CL_DEVICE_NOT_FOUND = -1;
public static final int CL_PLATFORM_VENDOR = 0x0903;
// cl_device_type
public static final int CL_DEVICE_TYPE_DEFAULT = (1 << 0);
public static final int CL_DEVICE_TYPE_CPU = (1 << 1);
public static final int CL_DEVICE_TYPE_GPU = (1 << 2);
public static final int CL_DEVICE_TYPE_ACCELERATOR = (1 << 3);
public static final int CL_DEVICE_TYPE_CUSTOM = (1 << 4);
public static final int CL_DEVICE_TYPE_ALL = 0xFFFFFFFF;
// cl_device_info
public static final int CL_DEVICE_NAME = 0x102B;
public static final int CL_DEVICE_GLOBAL_MEM_SIZE = 0x101F;
public static final int CL_DEVICE_BOARD_NAME_AMD = 0x4038;
public int clGetPlatformIDs(int num_entries, CLPlatformId.ByReference[] platforms, IntByReference num_platforms);
public int clGetPlatformInfo(CLPlatformId.ByReference platform, int param_name, long param_value_size, byte[] destination, long size_ret[]);
public int clGetDeviceIDs(CLPlatformId.ByReference platform, int param_name, int num_entries, CLDeviceId.ByReference[] devices, IntByReference device_count);
public int clGetDeviceInfo(CLDeviceId.ByReference device, int param_name, long param_value_size, byte[] destination, long size_ret[]);
public static class CLPlatformId extends Structure {
public static class ByReference extends CLPlatformId implements Structure.ByReference {
}
public int id;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "id" });
}
}
public static class CLDeviceId extends Structure {
public static class ByReference extends CLDeviceId implements Structure.ByReference {
}
public int id;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "id" });
}
}
}