澄清: 我将opencl计算函数的参数(例如内核范围、设备和显式副本)硬编码到我的每个项目中,但这很累,我决定编写一个完全动态的类,对数组进行分治并发送片段到不同的设备。目前,它可以构建支持 opencl 的设备列表,可以使用多个线程划分工作,使用另一个线程池进行计算并使用第三个线程池重新组装。(例如:1个线程划分,2个设备上的2个线程计算,1个线程将结果组装成原始数组)。线程驻留在 java 执行程序的线程池中。
问题:当它将一个 2M 元素的工作分成 256 块(每个计算 8k 元素)时,内核启动开销(在 gpu 上)变得超过执行时间的 %100。当我增加每个片段的大小(到 ~64k 或 128k)时,它会变得无性能意识,例如在弱设备上进行过多的工作并且速度会更慢。每个元素上的工作量也可能不相等(光线跟踪、粒子碰撞的树结构、可变循环迭代......)。我的 gpu 只能同时执行两个独立的内核,所以我无法成功隐藏分而治之碎片的读取-计算-写入。如果我简单地将所有工作分成两部分,那就更糟了。当元素数量达到 20M 时,内核启动开销将非常明显。
问题:我是否应该只将整个阵列复制到所有设备一次(而不是片段)(10 个设备的 10 个副本,可以使用重叠的计算和副本隐藏一些)并且在连续的 ndrangekernel 执行上只计算一次,或者将最小尺寸的部分复制到设备然后测试其性能,然后根据该微基准复制可变长度的元素数组?如何在执行次数与性能感知之间取得平衡,以实现光线追踪、粒子碰撞和排序算法的实时速度,以便我的 cpu 实际上可以帮助 gpu 而不是降低性能?
现在,它看起来像:
double[]a=new double[8192*256];
double[]b=new double[8192*256];
for(int i=0;i<a.length;i++)
{
a[i]=(double)(0.1*i);
b[i]=0;
}
AccContext acc=new AccContext("gpu cpu",new Object[]{a,b},Kernels.Trigonometry());
// balanced work
// here, Sin(a) results in b array.
acc.Compute("Sine");
// b[i]=Cos of Cos of .... Cos(Sin(a[i])); for each ith element of array.
for(int k=0;k<10;k++)
acc.Compute("Cosine");
// non-balanced work
// repeats taking sinuses for i times for each a[i]
acc.Compute("Sine_i_times");
系统: windows-7 64 位 + 支持 Opencl 1.2 的 gpu 和 cpu、jocl 和 java 64 位。所以我不能使用 Opencl 2.0。