我在ocl编程方面很新。
我有200万个多边形(每个4行 - 对于这个例子 - 但它是变体的)我需要找到1000个椭圆的交集。
我需要知道每个椭圆是否至少与一个多边形相交。
为此我创建了一个包含所有点的poylgons缓冲区,以及省略号缓冲区。
我的输出缓冲区是1000项int在所有项目中设置为0。并且内核将根据椭圆索引在右侧索引中设置1(当他找到交叉点时)。
我使用全局运行内核 - 2 dim,{2million,1000}。
__kernel void polygonsIntersectsEllipses( __global const Point* pts,
__global const Ellipse* ellipses,
__global int* output)
{
int polygonIdx = get_global_id(0);
int ellipseIdx = get_global_id(1);
if (<<isIntersects>>) {
output[ellipseIdx] = 1;
}
}
问题在于,一旦多边形中的一个与椭圆相交,我就不需要计算其余的多边形。
我曾尝试在交叉路口测试前检查output[ellipseIdx] != 0
,但性能没有太大变化。
我试图做单个暗淡的全局 - 给1000(椭圆)并在内核中运行数百万个多边形并在我找到它时停止,但仍然没有那么多变化。
我做错了吗?我可以加快这项操作吗? 任何提示?
修改
使用来自@Moises的提示并进行大量研究我将我的代码更改为运行200万次,单维度。使用小组工作项目。将我的所有结构更改为本机类型,跳过模数运算。基本上我可以将数据从全局复制到私有/本地内存,我做到了。
我的本地大小是我的设备CL_DEVICE_MAX_WORK_GROUP_SIZE
,在我的cpu&amp; gpu中是1024,所以在一次运行中我覆盖了我的所有1000个省略号。
主持人
size_t global = 1999872; // 2 million divided by 1024 - for the test
size_t local = 1024;
我的代码现在看起来像这样
__kernel void polygonsIntersectsEllipses( __global const float4* pts,
__global const float4* ellipses,
int ellipseCount,
__local float4* localEllipses,
__global int* output)
{
// Saving the ellipses to local memory
int localId = get_local_id(0);
if (localId < eCount)
localEllipses[localId] = ellipses[localId];
barrier(CLK_LOCAL_MEM_FENCE);
// Saving the current polygon into private memory
int polygonIdx = get_global_id(0);
float2 private_pts[5];
for (int currPtsIdx = 0; currPtsIdx < 4; currPtsIdx++)
{
private_pts[currPtsIdx] = pts[polygonIdx * 4 + currPtsIdx];
}
// saving the last point as first so i will not use modulus for cycling, in the intersection algorithm
private_pts[4] = private_pts[0];
// Run over all the ellipse in the local memory including checking if already there is an output
for (int ellipseIdx = 0; ellipseIdx < ellipseCount && output[ellipseIdx] == 0; ++ellipseIdx) {
if (<<isIntersects Using localEllipses array and private_pts>>) {
output[ellipseIdx] = 1;
}
}
}
结果
CPU没有那么大的改进 - 改变后的速度提高了1.1。
GPU - 6.5 倍快(我很激动)
我还有什么地方可以改善吗? 提醒一旦多边形中的一个与椭圆相交,我们就不需要检查其余的多边形。我怎么做 ?询问输出值的诀窍并不是真的有效 - 无论是否有表现都是相同的
答案 0 :(得分:1)
我明白你所有的2百万x1000个线程,读取自己的多边形数据和椭圆对吗?因此,对于每个多边形,每个线程读取相同内存位置的1000倍(使用多边形数据),不是吗?为了避免这种内存绑定行为,您只能创建2百万个线程并使用1000次迭代的for循环来迭代省略号的数量。或者是一个中间解决方案,具有2百万x64个线程的网格,其中每个线程为每个多边形计算16个椭圆。我不知道这些是否比你的解决方案更好,但它们避免了多余的内存访问。
此致 莫伊塞斯
答案 1 :(得分:0)
优化:
__global int* output
太多,请改用char
。甚至更好,使用二进制数组。 (二进制操作与全局读取相比很快)您不应该从每个线程output[ellipseIdx] == 0
的全局内存中再次读取。这非常慢,相反,在开头用elipses数据保存到本地内存。注意:只有在找到匹配的组之后启动本地组才能从加速中受益。然而,这将节省大量的全局读取,这比保存一些计算要好得多。此外,本地组无法从中受益,因为当工作项找到匹配项时,所有本地工作项都已经处理了该椭圆。
__ kernel void polygonsIntersectsEllipses(
__global const float4* pts,
__global const float4* ellipses,
int ellipseCount,
__local float4* localEllipses,
__local char* localOuts,
__global char* output){
// Saving the ellipses to local memory
int localId = get_local_id(0);
if (localId < eCount)
localOuts[localId] = output[localId];
barrier(CLK_LOCAL_MEM_FENCE);
if (localId < eCount && localOuts[localId]) // Do not copy elipses if we are not going to check them anyway
localEllipses[localId] = ellipses[localId];
barrier(CLK_LOCAL_MEM_FENCE);
// Saving the current polygon into private memory
int polygonIdx = get_global_id(0);
float2 private_pts[5];
for (int currPtsIdx = 0; currPtsIdx < 4; currPtsIdx++)
{
private_pts[currPtsIdx] = pts[polygonIdx * 4 + currPtsIdx];
}
// saving the last point as first so i will not use modulus for cycling, in the intersection algorithm
private_pts[4] = private_pts[0];
// Run over all the ellipse in the local memory including checking if already there is an output
for (int ellipseIdx = 0; ellipseIdx < ellipseCount; ++ellipseIdx) {
if (localOuts[ellipseIdx] == 0){
if (<<isIntersects Using localEllipses array and private_pts>>) {
localOuts[ellipseIdx] = 1;
}
barrier(CLK_LOCAL_MEM_FENCE);
if(localOuts[ellipseIdx] && localId == 0){
output[ellipseIdx] = 1;
}
}
}
}