我正在使用 jogamp jocl 库在 Java 中学习自己的 openCL。我的一项测试是创建 Mandelbrot 地图。我有四个测试:简单的串行、使用 Java 执行器接口的并行、针对单个设备的 openCL 和针对多个设备的 openCL。前三个可以,最后一个不行。当我将多设备的(正确)输出与多设备解决方案的不正确输出进行比较时,我注意到颜色大致相同,但最后一个的输出是乱码。我想我知道问题出在哪里,但我无法解决。
问题在于(恕我直言)openCL 使用矢量缓冲区并且我必须将输出转换为矩阵。我认为这个翻译是不正确的。我通过将 mandelbrot 映射划分为宽度 (xSize) 除以任务数并保留高度 (ySize) 的矩形来并行化代码。我认为我能够将该信息正确地传输到内核中,但是将其翻译回来是不正确的。
CLMultiContext mc = CLMultiContext.create (deviceList);
try
{
CLSimpleContextFactory factory = CLQueueContextFactory.createSimple (programSource);
CLCommandQueuePool<CLSimpleQueueContext> pool = CLCommandQueuePool.create (factory, mc);
IntBuffer dataC = Buffers.newDirectIntBuffer (xSize * ySize);
IntBuffer subBufferC = null;
int tasksPerQueue = 16;
int taskCount = pool.getSize () * tasksPerQueue;
int sliceWidth = xSize / taskCount;
int sliceSize = sliceWidth * ySize;
int bufferSize = sliceSize * taskCount;
double sliceX = (pXMax - pXMin) / (double) taskCount;
String kernelName = "Mandelbrot";
out.println ("sliceSize: " + sliceSize);
out.println ("sliceWidth: " + sliceWidth);
out.println ("sS*h:" + sliceWidth * ySize);
List<CLTestTask> tasks = new ArrayList<CLTestTask> (taskCount);
for (int i = 0; i < taskCount; i++)
{
subBufferC = Buffers.slice (dataC, i * sliceSize, sliceSize);
tasks.add (new CLTestTask (kernelName, i, sliceWidth, xSize, ySize, maxIterations,
pXMin + i * sliceX, pYMin, xStep, yStep, subBufferC));
} // for
pool.invokeAll (tasks);
// submit blocking immediately
for (CLTestTask task: tasks) pool.submit (task).get ();
// Ready read the buffer into the frequencies matrix
// according to me this is the part that goes wrong
int w = taskCount * sliceWidth;
for (int tc = 0; tc < taskCount; tc++)
{
int offset = tc * sliceWidth;
for (int y = 0; y < ySize; y++)
{
for (int x = offset; x < offset + sliceWidth; x++)
{
frequencies [y][x] = dataC.get (y * w + x);
} // for
} // for
} // for
pool.release();
最后一个循环是罪魁祸首,这意味着(我认为)内核编码和主机翻译之间存在不匹配。内核:
kernel void Mandelbrot
(
const int width,
const int height,
const int maxIterations,
const double x0,
const double y0,
const double stepX,
const double stepY,
global int *output
)
{
unsigned ix = get_global_id (0);
unsigned iy = get_global_id (1);
if (ix >= width) return;
if (iy >= height) return;
double r = x0 + ix * stepX;
double i = y0 + iy * stepY;
double x = 0;
double y = 0;
double magnitudeSquared = 0;
int iteration = 0;
while (magnitudeSquared < 4 && iteration < maxIterations)
{
double x2 = x*x;
double y2 = y*y;
y = 2 * x * y + i;
x = x2 - y2 + r;
magnitudeSquared = x2+y2;
iteration++;
}
output [iy * width + ix] = iteration;
}
最后一条语句将信息编码到向量中。单设备版本也使用此内核。唯一的区别是在多设备版本中我更改了宽度和 x0。正如您在 Java 代码中看到的那样,我xSize / number_of_tasks
以宽度和pXMin + i * sliceX
x0(而不是 pXMin)传输。
我现在已经工作了几天并且已经删除了很多错误,但是我现在看不到我做错了什么。非常感谢您的帮助。
编辑 1
@Huseyin 要求提供图片。由 openCL 单设备计算的第一个屏幕截图。
编辑 2
有一个关于我如何将缓冲区排入队列的问题。正如您在上面的代码中看到的那样,我有一个list<CLTestTask>
向其中添加任务并且缓冲区被排队的地方。CLTestTask 是一个内部类,您可以在下面找到它的代码。
最终类 CLTestTask 实现 CLTask { CLBuffer clBufferC = null; 缓冲区 bufferSliceC; 字符串内核名称;整数索引;整数切片宽度;整数宽度;整数高度;整数最大迭代次数;双 pXMin; 双 pYMin;双x_step;双 y_step;
public CLTestTask
(
String kernelName,
int index,
int sliceWidth,
int width,
int height,
int maxIterations,
double pXMin,
double pYMin,
double x_step,
double y_step,
Buffer bufferSliceC
)
{
this.index = index;
this.sliceWidth = sliceWidth;
this.width = width;
this.height = height;
this.maxIterations = maxIterations;
this.pXMin = pXMin;
this.pYMin = pYMin;
this.x_step = x_step;
this.y_step = y_step;
this.kernelName = kernelName;
this.bufferSliceC = bufferSliceC;
} /*** CLTestTask ***/
public Buffer execute (final CLSimpleQueueContext qc)
{
final CLCommandQueue queue = qc.getQueue ();
final CLContext context = qc.getCLContext ();
final CLKernel kernel = qc.getKernel (kernelName);
clBufferC = context.createBuffer (bufferSliceC);
out.println (pXMin + " " + sliceWidth);
kernel
.putArg (sliceWidth)
.putArg (height)
.putArg (maxIterations)
.putArg (pXMin) // + index * x_step)
.putArg (pYMin)
.putArg (x_step)
.putArg (y_step)
.putArg (clBufferC)
.rewind ();
queue
.put2DRangeKernel (kernel, 0, 0, sliceWidth, height, 0, 0)
.putReadBuffer (clBufferC, true);
return clBufferC.getBuffer ();
} /*** execute ***/
} /*** Inner Class: CLTestTask ***/