summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Bien <[email protected]>2011-07-18 21:22:23 +0200
committerMichael Bien <[email protected]>2011-07-18 21:22:23 +0200
commit11ab131ed1d8a743b8987ccc5b404198a42f4749 (patch)
tree77a26ab0f396e1f11bbf825d40f3024f81407301
parent8c8c3d70db0c1ee78a9598e695a54441c49384e5 (diff)
initial subdevice support.
-rw-r--r--src/com/jogamp/opencl/CLDevice.java171
-rw-r--r--src/com/jogamp/opencl/CLException.java39
-rw-r--r--src/com/jogamp/opencl/CLSubDevice.java170
-rw-r--r--test/com/jogamp/opencl/HighLevelBindingTest.java64
4 files changed, 438 insertions, 6 deletions
diff --git a/src/com/jogamp/opencl/CLDevice.java b/src/com/jogamp/opencl/CLDevice.java
index 4705d482..755f4745 100644
--- a/src/com/jogamp/opencl/CLDevice.java
+++ b/src/com/jogamp/opencl/CLDevice.java
@@ -28,9 +28,16 @@
package com.jogamp.opencl;
+import com.jogamp.opencl.CLSubDevice.Partition;
+import com.jogamp.opencl.CLSubDevice.AffinityDomain;
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.nio.NativeSizeBuffer;
+import com.jogamp.opencl.llb.CL;
import com.jogamp.opencl.util.CLUtil;
import com.jogamp.opencl.spi.CLInfoAccessor;
import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -69,6 +76,85 @@ public class CLDevice extends CLObject {
this.deviceInfo = platform.getAccessorFactory().createDeviceInfoAccessor(platform.getDeviceBinding(), id);
}
+ /**
+ * Split the aggregate device into as many smaller aggregate devices as can be created, each containing N
+ * compute units. The value N is passed as the value accompanying this property.
+ * If N does not divide evenly into {@link #getMaxComputeUnits() } then the remaining compute units are
+ * not used.
+ */
+ public CLSubDevice[] createSubDevicesEqually(int computeUnitsPerDevice) {
+
+ LongBuffer props = Buffers.newDirectLongBuffer(3);
+ props.put(CL_DEVICE_PARTITION_EQUALLY_EXT).put(computeUnitsPerDevice).put(0).rewind();
+
+ return createSubDevices(props);
+ }
+
+ /**
+ * For each non-zero value in the list, a sub-device is created with N
+ * compute units in it.
+ */
+ public CLSubDevice[] createSubDevicesByCount(int... computeUnitsArray) {
+
+ if(computeUnitsArray.length == 0) {
+ throw new IllegalArgumentException("list was empty");
+ }
+
+ LongBuffer props = Buffers.newDirectLongBuffer(computeUnitsArray.length+3);
+ props.put(CL_DEVICE_PARTITION_BY_COUNTS_EXT);
+ for (int units : computeUnitsArray) {
+ props.put(units);
+ }
+ props.put(0).put(0).rewind();
+ return createSubDevices(props);
+ }
+
+ public CLSubDevice[] createSubDevicesByDomain(AffinityDomain domain) {
+
+ LongBuffer props = Buffers.newDirectLongBuffer(3);
+ props.put(CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT).put(domain.TYPE).put(0).rewind();
+ return createSubDevices(props);
+ }
+
+ /**
+ * Creates a devise from the supplied compute unit indices.
+ * An individual compute unit name may not appear more than once in the sub-device description.
+ */
+ public CLSubDevice createSubDeviceByIndex(int... computeUnitsIndexArray) {
+
+ if(computeUnitsIndexArray.length == 0) {
+ throw new IllegalArgumentException("list was empty");
+ }
+
+ LongBuffer props = Buffers.newDirectLongBuffer(computeUnitsIndexArray.length+3);
+ props.put(CL_DEVICE_PARTITION_BY_NAMES_EXT);
+ for (int units : computeUnitsIndexArray) {
+ props.put(units);
+ }
+ props.put(-1).put(0).rewind();
+ return createSubDevices(props)[0];
+ }
+
+ private CLSubDevice[] createSubDevices(LongBuffer props) {
+
+ CL cl = CLPlatform.getLowLevelCLInterface();
+
+ IntBuffer ib = Buffers.newDirectIntBuffer(1);
+ int ret = cl.clCreateSubDevicesEXT(ID, props, 0, null, ib);
+ CLException.checkForError(ret, "create sub devices failed");
+
+ int count = ib.get(0);
+ NativeSizeBuffer ids = NativeSizeBuffer.allocateDirect(count);
+ ret = cl.clCreateSubDevicesEXT(ID, props, count, ids, null);
+ CLException.checkForError(ret, "create sub devices failed");
+
+ CLSubDevice[] devices = new CLSubDevice[count];
+ for (int i = 0; i < devices.length; i++) {
+ devices[i] = CLSubDevice.createSubDevice(this, ids.get());
+ }
+ return devices;
+ }
+
public CLCommandQueue createCommandQueue() {
return createCommandQueue(0);
}
@@ -581,6 +667,50 @@ public class CLDevice extends CLObject {
}
/**
+ * Returns all supported partition types for device fission or an empty list if fission is not possible.
+ */
+ @CLProperty("CL_DEVICE_PARTITION_TYPES")
+ public EnumSet<Partition> getPartitionTypes() {
+
+ if(!isFissionSupported()) {
+ return EnumSet.noneOf(Partition.class);
+ }
+ LongBuffer types = getInfoLongs(CL_DEVICE_PARTITION_TYPES_EXT);
+
+ List<Partition> list = new ArrayList<Partition>();
+ while(types.hasRemaining()) {
+ Partition type = Partition.valueOf((int)types.get());
+ if(type != null) {
+ list.add(type);
+ }
+ }
+ return EnumSet.copyOf(list);
+ }
+
+ /**
+ * Returns all supported partition affinity domains which can be used to partition a device.
+ * @see #createSubDevicesByDomain(com.jogamp.opencl.CLSubDevice.AffinityDomain)
+ */
+ @CLProperty("CL_DEVICE_AFFINITY_DOMAINS")
+ public EnumSet<AffinityDomain> getAffinityDomains() {
+
+ if(!getPartitionTypes().contains(Partition.DOMAIN)) {
+ return EnumSet.noneOf(AffinityDomain.class);
+ }
+
+ LongBuffer types = getInfoLongs(CL_DEVICE_AFFINITY_DOMAINS_EXT);
+
+ List<AffinityDomain> list = new ArrayList<AffinityDomain>();
+ while(types.hasRemaining()) {
+ AffinityDomain type = AffinityDomain.valueOf((int)types.get());
+ if(type != null) {
+ list.add(type);
+ }
+ }
+ return EnumSet.copyOf(list);
+ }
+
+ /**
* Returns true if this device is available.
*/
@CLProperty("CL_DEVICE_AVAILABLE")
@@ -642,7 +772,6 @@ public class CLDevice extends CLObject {
public boolean isGLMemorySharingSupported() {
return isExtensionAvailable("cl_khr_gl_sharing") || isExtensionAvailable("cl_APPLE_gl_sharing");
}
-
/**
* Returns true if the extension is supported on this device.
* @see #getExtensions()
@@ -650,7 +779,22 @@ public class CLDevice extends CLObject {
public boolean isExtensionAvailable(String extension) {
return getExtensions().contains(extension);
}
-
+
+ /**
+ * Returns true if this device can be split up in sub-devices.
+ */
+ @CLProperty("cl_ext_device_fission")
+ public boolean isFissionSupported() {
+ return isExtensionAvailable("cl_ext_device_fission");
+ }
+
+ /**
+ * Returns false.
+ */
+ public boolean isSubDevice() {
+ return false;
+ }
+
/**
* Returns {@link ByteOrder#LITTLE_ENDIAN} or {@link ByteOrder#BIG_ENDIAN}.
*/
@@ -694,12 +838,27 @@ public class CLDevice extends CLObject {
return deviceInfo;
}
+ private LongBuffer getInfoLongs(int flag) {
+
+ CL cl = CLPlatform.getLowLevelCLInterface();
+
+ NativeSizeBuffer nsb = NativeSizeBuffer.allocateDirect(1);
+ int ret = cl.clGetDeviceInfo(ID, flag, 0, null, nsb);
+ CLException.checkForError(ret, "clGetDeviceInfo failed");
+
+ LongBuffer types = Buffers.newDirectByteBuffer((int)nsb.get(0)).asLongBuffer();
+ ret = cl.clGetDeviceInfo(ID, flag, types.capacity()*Buffers.SIZEOF_LONG, types, null);
+ CLException.checkForError(ret, "clGetDeviceInfo failed");
+
+ return types;
+ }
+
@Override
public String toString() {
- return "CLDevice [id: " + ID
- + " name: " + getName()
- + " type: " + getType()
- + " profile: " + getProfile()+"]";
+ return getClass().getSimpleName()+" [id: " + ID
+ + " name: " + getName()
+ + " type: " + getType()
+ + " profile: " + getProfile()+"]";
}
@Override
diff --git a/src/com/jogamp/opencl/CLException.java b/src/com/jogamp/opencl/CLException.java
index f776730b..0d66db69 100644
--- a/src/com/jogamp/opencl/CLException.java
+++ b/src/com/jogamp/opencl/CLException.java
@@ -147,6 +147,9 @@ public class CLException extends RuntimeException {
case CL_PLATFORM_NOT_FOUND_KHR: return "CL_PLATFORM_NOT_FOUND_KHR";
case CL_MISALIGNED_SUB_BUFFER_OFFSET: return "CL_MISALIGNED_SUB_BUFFER_OFFSET";
case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST";
+ case CL_DEVICE_PARTITION_FAILED_EXT: return "CL_DEVICE_PARTITION_FAILED";
+ case CL_INVALID_PARTITION_COUNT_EXT: return "CL_INVALID_PARTITION_COUNT";
+ case CL_INVALID_PARTITION_NAME_EXT: return "CL_INVALID_PARTITION_NAME";
default: return null;
}
}
@@ -204,6 +207,9 @@ public class CLException extends RuntimeException {
case CL_PLATFORM_NOT_FOUND_KHR: return new CLPlatformNotFoundKhrException(message);
case CL_MISALIGNED_SUB_BUFFER_OFFSET: return new CLMisalignedSubBufferOffsetException(message);
case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: return new CLExecStatusErrorForEventsInWaitListException(message);
+ case CL_DEVICE_PARTITION_FAILED_EXT: return new CLDevicePartitionFailedException(message);
+ case CL_INVALID_PARTITION_COUNT_EXT: return new CLInvalidPartitionCountException(message);
+ case CL_INVALID_PARTITION_NAME_EXT: return new CLInvalidPartitionNameException(message);
default: return null;
}
}
@@ -769,4 +775,37 @@ public class CLException extends RuntimeException {
}
}
+ /**
+ * {@link CLException} thrown on CL.CL_DEVICE_PARTITION_FAILED_EXT errors.
+ * @author Michael Bien
+ */
+ public final static class CLDevicePartitionFailedException extends CLException {
+ private static final long serialVersionUID = CLException.serialVersionUID+CL_DEVICE_PARTITION_FAILED_EXT;
+ public CLDevicePartitionFailedException(String message) {
+ super(CL_DEVICE_PARTITION_FAILED_EXT, "CL_DEVICE_PARTITION_FAILED", message);
+ }
+ }
+
+ /**
+ * {@link CLException} thrown on CL.CL_INVALID_PARTITION_COUNT_EXT errors.
+ * @author Michael Bien
+ */
+ public final static class CLInvalidPartitionCountException extends CLException {
+ private static final long serialVersionUID = CLException.serialVersionUID+CL_INVALID_PARTITION_COUNT_EXT;
+ public CLInvalidPartitionCountException(String message) {
+ super(CL_INVALID_PARTITION_COUNT_EXT, "CL_INVALID_PARTITION_COUNT", message);
+ }
+ }
+
+ /**
+ * {@link CLException} thrown on CL.CL_INVALID_PARTITION_NAME_EXT errors.
+ * @author Michael Bien
+ */
+ public final static class CLInvalidPartitionNameException extends CLException {
+ private static final long serialVersionUID = CLException.serialVersionUID+CL_INVALID_PARTITION_NAME_EXT;
+ public CLInvalidPartitionNameException(String message) {
+ super(CL_INVALID_PARTITION_NAME_EXT, "CL_INVALID_PARTITION_NAME", message);
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/com/jogamp/opencl/CLSubDevice.java b/src/com/jogamp/opencl/CLSubDevice.java
new file mode 100644
index 00000000..68cbe9dc
--- /dev/null
+++ b/src/com/jogamp/opencl/CLSubDevice.java
@@ -0,0 +1,170 @@
+/*
+ * Created on Wednesday, July 13 2011 00:50
+ */
+package com.jogamp.opencl;
+
+import com.jogamp.opencl.llb.CL;
+
+import static com.jogamp.opencl.llb.CL.*;
+
+/**
+ * A subdevice created through device fission.
+ * A subdevice can be used like any other device but must be released if no longer needed.
+ * <p>
+ * possible usecases for device fission:
+ * <ul>
+ * <li> To reserve part of the device for use for high priority / latency-sensitive tasks </li>
+ * <li> To more directly control the assignment of work to individual compute units </li>
+ * <li> To subdivide compute devices along some shared hardware feature like a cache </li>
+ * </ul>
+ * </p>
+ * @see CLDevice#createSubDevicesEqually(int)
+ * @see CLDevice#createSubDevicesByCount(int[])
+ * @see CLDevice#createSubDeviceByIndex(int[])
+ * @see CLDevice#createSubDevicesByDomain(com.jogamp.opencl.CLSubDevice.AffinityDomain)
+ * @author Michael Bien
+ */
+public class CLSubDevice extends CLDevice implements CLResource {
+
+ private volatile boolean released;
+
+ private final CLDevice parent;
+
+ private CLSubDevice(CLDevice parent, CLContext context, long id) {
+ super(context, id);
+ this.parent = parent;
+ }
+
+ private CLSubDevice(CLDevice parent, CLPlatform platform, long id) {
+ super(platform, id);
+ this.parent = parent;
+ }
+
+ static CLSubDevice createSubDevice(CLDevice device, long id) {
+ if(device.context == null) {
+ return new CLSubDevice(device, device.getPlatform(), id);
+ }else{
+ return new CLSubDevice(device, device.getContext(), id);
+ }
+ }
+
+ /**
+ * Returns the parent device which may be a CLDevice or another CLSubDevice.
+ */
+ public CLDevice getParent() {
+ return parent;
+ }
+
+ @Override
+ public void release() {
+ if(released) {
+ throw new RuntimeException("already released");
+ }
+ released = true;
+ CL cl = CLPlatform.getLowLevelCLInterface();
+ int ret = cl.clReleaseDeviceEXT(ID);
+ CLException.checkForError(ret, "release failed");
+ }
+
+ @Override
+ public boolean isReleased() {
+ return released;
+ }
+
+ @Override
+ public boolean isSubDevice() {
+ return true;
+ }
+
+
+ /**
+ * Sub device affinity domains.
+ * @see CLDevice#createSubDevicesByDomain(com.jogamp.opencl.CLSubDevice.AffinityDomain)
+ */
+ public enum AffinityDomain {
+
+ L1_CACHE(CL_AFFINITY_DOMAIN_L1_CACHE_EXT),
+
+ L2_CACHE(CL_AFFINITY_DOMAIN_L2_CACHE_EXT),
+
+ L3_CACHE(CL_AFFINITY_DOMAIN_L3_CACHE_EXT),
+
+ L4_CACHE(CL_AFFINITY_DOMAIN_L4_CACHE_EXT),
+
+ NUMA(CL_AFFINITY_DOMAIN_NUMA_EXT),
+
+ NEXT_FISSIONABLE(CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT);
+
+ /**
+ * Value of wrapped OpenCL value.
+ */
+ public final int TYPE;
+
+ private AffinityDomain(int type) {
+ this.TYPE = type;
+ }
+
+ /**
+ * Returns the matching AffinityDomain for the given cl flag.
+ */
+ public static AffinityDomain valueOf(int domain) {
+ AffinityDomain[] values = AffinityDomain.values();
+ for (AffinityDomain value : values) {
+ if(value.TYPE == domain)
+ return value;
+ }
+ return null;
+ }
+
+ }
+
+ /**
+ * Sub device partition styles.
+ * @see CLDevice#getPartitionTypes()
+ */
+ public enum Partition {
+
+ /**
+ * @see CLDevice#createSubDevicesEqually(int)
+ */
+ EQUALLY(CL_DEVICE_PARTITION_EQUALLY_EXT),
+
+ /**
+ * @see CLDevice#createSubDevicesByCount(int[])
+ */
+ COUNTS(CL_DEVICE_PARTITION_BY_COUNTS_EXT),
+
+ /**
+ * @see CLDevice#createSubDeviceByIndex(int[])
+ */
+ NAMES(CL_DEVICE_PARTITION_BY_NAMES_EXT),
+
+ /**
+ * @see CLDevice#createSubDevicesByDomain(com.jogamp.opencl.CLSubDevice.AffinityDomain)
+ */
+ DOMAIN(CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT);
+
+ /**
+ * Value of wrapped OpenCL value.
+ */
+ public final int FLAG;
+
+ private Partition(int type) {
+ this.FLAG = type;
+ }
+
+ /**
+ * Returns the matching AffinityDomain for the given cl flag.
+ */
+ public static Partition valueOf(int domain) {
+ Partition[] values = Partition.values();
+ for (Partition value : values) {
+ if(value.FLAG == domain)
+ return value;
+ }
+ return null;
+ }
+
+ }
+
+}
diff --git a/test/com/jogamp/opencl/HighLevelBindingTest.java b/test/com/jogamp/opencl/HighLevelBindingTest.java
index fe6bef51..2e781ff5 100644
--- a/test/com/jogamp/opencl/HighLevelBindingTest.java
+++ b/test/com/jogamp/opencl/HighLevelBindingTest.java
@@ -28,6 +28,8 @@
package com.jogamp.opencl;
+import com.jogamp.opencl.CLSubDevice.AffinityDomain;
+import com.jogamp.opencl.CLSubDevice.Partition;
import com.jogamp.opencl.CLMemory.Mem;
import com.jogamp.opencl.CLMemory.GLObjectType;
import com.jogamp.opencl.CLSampler.AddressingMode;
@@ -98,6 +100,14 @@ public class HighLevelBindingTest {
assertEquals(e, Capabilities.valueOf(e.CAPS));
}
+ // CLSubDevice enums
+ for (Partition e : Partition.values()) {
+ assertEquals(e, Partition.valueOf(e.FLAG));
+ }
+ for (AffinityDomain e : AffinityDomain.values()) {
+ assertEquals(e, AffinityDomain.valueOf(e.TYPE));
+ }
+
// CLMemory enums
for (Mem e : Mem.values()) {
assertEquals(e, Mem.valueOf(e.CONFIG));
@@ -212,6 +222,60 @@ public class HighLevelBindingTest {
}
@Test
+ public void subDeviceTest() {
+
+ out.println(" - - - highLevelTest; device fission - - - ");
+
+ CLPlatform platform = CLPlatform.getDefault(version(CL_1_1), type(CPU));
+
+ CLDevice[] devices = platform.listCLDevices();
+
+ for (CLDevice device : devices) {
+
+ out.println(device.getPartitionTypes());
+// out.println(device.getAffinityDomains());
+
+ if(device.isFissionSupported() && device.getMaxComputeUnits() >= 2) {
+
+ CLSubDevice[] subs = device.createSubDevicesEqually(2);
+ assertNotNull(subs);
+ assertEquals(device.getMaxComputeUnits()/2, subs.length);
+
+ for (CLSubDevice sub : subs) {
+
+ assertTrue(sub.isSubDevice());
+ assertFalse(sub.isReleased());
+ assertEquals(2, sub.getMaxComputeUnits());
+
+ sub.release();
+ assertTrue(sub.isReleased());
+ }
+
+ subs = device.createSubDevicesByCount(1, 1);
+ assertNotNull(subs);
+ assertEquals(2, subs.length);
+ for (CLSubDevice sub : subs) {
+ sub.release();
+ }
+
+ /* does not work. looks like a driver bug... waiting for reply
+ subs = device.createSubDevicesByDomain(AffinityDomain.NEXT_FISSIONABLE);
+ assertNotNull(subs);
+ assertTrue(subs.length > 0);
+ for (CLSubDevice sub : subs) {
+ sub.release();
+ }
+
+ CLSubDevice sub = device.createSubDeviceByIndex(0, 1);
+ assertNotNull(sub);
+ sub.release();
+ */
+ }
+ }
+
+ }
+
+ @Test
public void createContextTest() {
out.println(" - - - highLevelTest; create context - - - ");