From c8ae2569a4d90e79b9af83acdd50c1be2ecab9f0 Mon Sep 17 00:00:00 2001 From: Michael Bien Date: Sun, 30 May 2010 04:01:57 +0200 Subject: improved concurrent load test (disabled by default) and confirmed that cl programs can't be built concurrently. CLProgram uses ReentrantLock to put synchronous and asynchronous calls to clBuildProgram(...) in a squence. --- src/com/jogamp/opencl/CLProgram.java | 23 ++++-- test/com/jogamp/opencl/LowLevelBindingTest.java | 103 +++++++++++++++++------- test/com/jogamp/opencl/gl/CLGLTest.java | 2 + 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/com/jogamp/opencl/CLProgram.java b/src/com/jogamp/opencl/CLProgram.java index b6c1d7a2..7ccc3bc8 100644 --- a/src/com/jogamp/opencl/CLProgram.java +++ b/src/com/jogamp/opencl/CLProgram.java @@ -15,6 +15,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Set; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import static com.jogamp.opencl.CLException.*; import static com.jogamp.opencl.CL.*; @@ -30,7 +31,7 @@ import static com.jogamp.common.nio.Buffers.*; */ public class CLProgram extends CLObject implements CLResource { -// private final static Object buildLock = new Object(); + private final static ReentrantLock buildLock = new ReentrantLock(); private final Set kernels; private Map buildStatusMap; @@ -317,6 +318,7 @@ public class CLProgram extends CLObject implements CLResource { if(listener != null) { callback = new BuildProgramCallback() { public void buildFinished(long cl_program) { + buildLock.unlock(); listener.buildFinished(CLProgram.this); } }; @@ -324,10 +326,21 @@ public class CLProgram extends CLObject implements CLResource { // Build the program int ret = 0; - // building programs is not threadsafe -// synchronized(buildLock) { - ret = cl.clBuildProgram(ID, count, deviceIDs, options, callback); -// } + + // spec: building programs is not threadsafe, we are locking the API call to + // make sure only one thread calls it at a time until it completes (asynchronous or synchronously). + { + buildLock.lock(); + boolean exception = true; + try{ + ret = cl.clBuildProgram(ID, count, deviceIDs, options, callback); + exception = false; + }finally{ + if(callback == null || exception) { + buildLock.unlock(); + } + } + } if(ret != CL_SUCCESS) { throw newException(ret, "\n"+getBuildLog()); diff --git a/test/com/jogamp/opencl/LowLevelBindingTest.java b/test/com/jogamp/opencl/LowLevelBindingTest.java index 9e4082aa..af72c46f 100644 --- a/test/com/jogamp/opencl/LowLevelBindingTest.java +++ b/test/com/jogamp/opencl/LowLevelBindingTest.java @@ -1,13 +1,21 @@ package com.jogamp.opencl; +import java.util.Random; import com.jogamp.opencl.impl.BuildProgramCallback; import com.jogamp.common.nio.Int64Buffer; import com.jogamp.common.nio.PointerBuffer; -import com.jogamp.opencl.impl.CLImpl; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.junit.BeforeClass; import org.junit.Test; @@ -47,6 +55,8 @@ public class LowLevelBindingTest { + " c[iGID] = iGID; \n" + "} \n"; + private int ELEMENT_COUNT = 11444777; + @BeforeClass public static void setUpClass() throws Exception { @@ -205,30 +215,34 @@ public class LowLevelBindingTest { .rewind(); long context = cl.clCreateContextFromType(properties, CL.CL_DEVICE_TYPE_ALL, null, null); out.println("context handle: "+context); + checkError("on clCreateContextFromType", ret); ret = cl.clGetContextInfo(context, CL.CL_CONTEXT_DEVICES, 0, null, longBuffer); checkError("on clGetContextInfo", ret); - int sizeofLong = is32Bit()?4:8; - out.println("context created with " + longBuffer.get(0)/sizeofLong + " devices"); + int deviceCount = (int) (longBuffer.get(0) / (is32Bit() ? 4 : 8)); + out.println("context created with " + deviceCount + " devices"); ByteBuffer bb = newDirectByteBuffer(4096); ret = cl.clGetContextInfo(context, CL.CL_CONTEXT_DEVICES, bb.capacity(), bb, null); checkError("on clGetContextInfo", ret); - for (int i = 0; i < longBuffer.get(0)/sizeofLong; i++) { - out.println("device id: "+bb.getLong()); + for (int i = 0; i < deviceCount; i++) { + out.println("device id: " + (is32Bit()?bb.getInt():bb.getLong())); } - long firstDeviceID = bb.getLong(0); + // use a random device + int offset = new Random().nextInt(deviceCount); + out.println("using device# " + offset); + offset *= (is32Bit() ? 4 : 8); + long device = is32Bit()?bb.getInt(offset):bb.getLong(offset); // Create a command-queue - long commandQueue = cl.clCreateCommandQueue(context, firstDeviceID, 0, intBuffer); + long commandQueue = cl.clCreateCommandQueue(context, device, 0, intBuffer); checkError("on clCreateCommandQueue", intBuffer.get(0)); - int elementCount = 11444777; // Length of float arrays to process (odd # for illustration) int localWorkSize = 256; // set and log Global and Local work size dimensions - int globalWorkSize = roundUp(localWorkSize, elementCount); // rounded up to the nearest multiple of the LocalWorkSize + int globalWorkSize = roundUp(localWorkSize, ELEMENT_COUNT); // rounded up to the nearest multiple of the LocalWorkSize out.println("allocateing buffers of size: "+globalWorkSize); @@ -248,6 +262,7 @@ public class LowLevelBindingTest { // Create the program Int64Buffer lengths = (Int64Buffer)Int64Buffer.allocateDirect(1).put(programSource.length()); final long program = cl.clCreateProgramWithSource(context, 1, new String[] {programSource}, lengths, intBuffer); + out.println("program id: "+program); checkError("on clCreateProgramWithSource", intBuffer.get(0)); // tests if the callback is called @@ -262,16 +277,20 @@ public class LowLevelBindingTest { } }; - // Build the program - ret = cl.clBuildProgram(program, 0, null, null, callback); - latch.countDown(); // TODO remove if callbacks are enabled again - checkError("on clBuildProgram", ret); + // spec: building programs is not threadsafe (see loadtest) + synchronized(CLProgram.class) { + // Build the program + ret = cl.clBuildProgram(program, 0, null, null, callback); + latch.countDown(); // TODO remove if callbacks are enabled again + checkError("on clBuildProgram", ret); - out.println("waiting for program to build..."); - latch.await(); + out.println("waiting for program to build..."); + latch.await(); + } out.println("done"); // Read program infos + bb.rewind(); ret = cl.clGetProgramInfo(program, CL.CL_PROGRAM_NUM_DEVICES, bb.capacity(), bb, null); checkError("on clGetProgramInfo1", ret); out.println("program associated with "+bb.getInt(0)+" device(s)"); @@ -286,24 +305,25 @@ public class LowLevelBindingTest { out.println("program source:\n" + clString2JavaString(bb, (int)longBuffer.get(0))); // Check program status - ret = cl.clGetProgramBuildInfo(program, firstDeviceID, CL.CL_PROGRAM_BUILD_STATUS, bb.capacity(), bb, null); + ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_STATUS, bb.capacity(), bb, null); checkError("on clGetProgramBuildInfo1", ret); out.println("program build status: " + CLProgram.Status.valueOf(bb.getInt(0))); assertEquals("build status", CL.CL_BUILD_SUCCESS, bb.getInt(0)); // Read build log - ret = cl.clGetProgramBuildInfo(program, firstDeviceID, CL.CL_PROGRAM_BUILD_LOG, 0, null, longBuffer); + ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, 0, null, longBuffer); checkError("on clGetProgramBuildInfo2", ret); out.println("program log length: " + longBuffer.get(0)); bb.rewind(); - ret = cl.clGetProgramBuildInfo(program, firstDeviceID, CL.CL_PROGRAM_BUILD_LOG, bb.capacity(), bb, null); + ret = cl.clGetProgramBuildInfo(program, device, CL.CL_PROGRAM_BUILD_LOG, bb.capacity(), bb, null); checkError("on clGetProgramBuildInfo3", ret); out.println("log:\n" + clString2JavaString(bb, (int)longBuffer.get(0))); // Create the kernel long kernel = cl.clCreateKernel(program, "VectorAdd", intBuffer); + out.println("kernel id: "+kernel); checkError("on clCreateKernel", intBuffer.get(0)); // srcA.limit(elementCount*SIZEOF_FLOAT); @@ -316,7 +336,7 @@ public class LowLevelBindingTest { ret = cl.clSetKernelArg(kernel, 0, is32Bit()?SIZEOF_INT:SIZEOF_LONG, wrap(devSrcA)); checkError("on clSetKernelArg0", ret); ret = cl.clSetKernelArg(kernel, 1, is32Bit()?SIZEOF_INT:SIZEOF_LONG, wrap(devSrcB)); checkError("on clSetKernelArg1", ret); ret = cl.clSetKernelArg(kernel, 2, is32Bit()?SIZEOF_INT:SIZEOF_LONG, wrap(devDst)); checkError("on clSetKernelArg2", ret); - ret = cl.clSetKernelArg(kernel, 3, SIZEOF_INT, wrap(elementCount)); checkError("on clSetKernelArg3", ret); + ret = cl.clSetKernelArg(kernel, 3, SIZEOF_INT, wrap(ELEMENT_COUNT)); checkError("on clSetKernelArg3", ret); out.println("used device memory: "+ (srcA.capacity()+srcB.capacity()+dest.capacity())/1000000 +"MB"); @@ -359,24 +379,53 @@ public class LowLevelBindingTest { ret = cl.clReleaseKernel(kernel); checkError("on clReleaseKernel", ret); - ret = cl.clUnloadCompiler(); - checkError("on clUnloadCompiler", ret); +// ret = cl.clUnloadCompiler(); +// checkError("on clUnloadCompiler", ret); ret = cl.clReleaseContext(context); checkError("on clReleaseContext", ret); } -/* - @Test - public void loadTest() { + +// @Test + public void loadTest() throws InterruptedException { //for memory leak detection; e.g watch out for "out of host memory" errors out.println(" - - - loadTest - - - "); + + ExecutorService pool = Executors.newFixedThreadPool(8); + List> tasks = new ArrayList>(); + for(int i = 0; i < 100; i++) { - out.println("###iteration "+i); - lowLevelVectorAddTest(); + final int n = i; + tasks.add(new Callable() { + public Object call() { + try { + out.println("###start iteration " + n); + LowLevelBindingTest test = new LowLevelBindingTest(); + test.ELEMENT_COUNT = 123456; + test.lowLevelVectorAddTest(); + out.println("###end iteration " + n); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + return null; + } + }); } + + for (Future future : pool.invokeAll(tasks)) { + try { + future.get(); + } catch (ExecutionException ex) { + ex.printStackTrace(); + pool.shutdown(); + fail(); + } + } + pool.shutdown(); + pool.awaitTermination(300, TimeUnit.SECONDS); } -*/ + private ByteBuffer wrap(long value) { return (ByteBuffer) newDirectByteBuffer(8).putLong(value).rewind(); } diff --git a/test/com/jogamp/opencl/gl/CLGLTest.java b/test/com/jogamp/opencl/gl/CLGLTest.java index 8bcb195f..b7dfe1dc 100644 --- a/test/com/jogamp/opencl/gl/CLGLTest.java +++ b/test/com/jogamp/opencl/gl/CLGLTest.java @@ -51,6 +51,8 @@ public class CLGLTest { glWindow.setVisible(true); glcontext = glWindow.getContext(); + out.println("useing glcontext:"); + out.println(glcontext); } @AfterClass -- cgit v1.2.3