diff options
author | Michael Bien <[email protected]> | 2011-07-18 21:22:23 +0200 |
---|---|---|
committer | Michael Bien <[email protected]> | 2011-07-18 21:22:23 +0200 |
commit | 11ab131ed1d8a743b8987ccc5b404198a42f4749 (patch) | |
tree | 77a26ab0f396e1f11bbf825d40f3024f81407301 | |
parent | 8c8c3d70db0c1ee78a9598e695a54441c49384e5 (diff) |
initial subdevice support.
-rw-r--r-- | src/com/jogamp/opencl/CLDevice.java | 171 | ||||
-rw-r--r-- | src/com/jogamp/opencl/CLException.java | 39 | ||||
-rw-r--r-- | src/com/jogamp/opencl/CLSubDevice.java | 170 | ||||
-rw-r--r-- | test/com/jogamp/opencl/HighLevelBindingTest.java | 64 |
4 files changed, 438 insertions, 6 deletions
diff --git a/src/com/jogamp/opencl/CLDevice.java b/src/com/jogamp/opencl/CLDevice.java index 4705d48..755f474 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 f776730..0d66db6 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 0000000..68cbe9d --- /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 fe6bef5..2e781ff 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 - - - "); |