是否有 OpenCL std::queue 等价物
is there an OpenCL std::queue equivalent
我有一些代码与此类似:
std::queue<unsigned> q;
q.push(array[0]);
while (!q.empty())
{
unsigned index = q.front();
q.pop();
if(index >= someval){
index = index - someval;
q.push(array[index]);
}
else{
//do something
}
}
}
我想将其移至 OpenCL 内核,我将如何以最有效的方式复制队列的功能。
目前我用一个固定大小的数组来实现它,它监视队列中元素的头部和计数。请问有没有更优雅的解决方案
谢谢
您应该检查 SYCL:https://www.khronos.org/sycl 其中 C++ 模板函数可以包含主机和设备代码以构建使用 OpenCL 加速的复杂算法。如果你不能使用它,如果你甚至没有 opencl 2.0 的设备:
1.2 版没有任何等效项。但是在评论中,你说:
Not sure that I understand what you mean but this would need to be individual to all threads on the GPU
那么就不需要线程间通信了,为了缓存+内存+计算效率,使用循环缓冲区实现一个FIFO应该很简单,只是不要溢出它(最多64个元素)并且不要't underflow(弹出次数多于推送次数,但易于实现边界检查(性能损失)):
推送
bool push(__private uint * stack, uint value)
{
// pushing from bot, so you can pop it from top later (FIFO)
// circular buffer for top performance
uint bufLen=64;
// zeroth element is counter for newest added element
// first element is oldest element
// circular buffer
uint nextIndex=(stack[0]%bufLen+2); // +2 because of top-bot headers
// if overflows, it overwrites oldest elements one by one
stack[nextIndex]=value;
// if overflows, it still increments
stack[0]++;
// simple and fast
return true;
}
检查是否为空
bool empty(__private uint * stack)
{
// tricky if you overflow both
return (stack[0]==stack[1]);
}
前值
uint front(__private uint * stack)
{
uint bufLen=64;
// oldest element value (top)
uint ptr=stack[1]%bufLen+2; // circular adr + 2 header
return stack[ptr];
}
爆裂
uint pop(__private uint * stack)
{
uint bufLen=64;
uint ptr=stack[1]%bufLen+2;
// pop from top (oldest)
uint returnValue=stack[ptr];
stack[ptr]=0;
// this will be new top ctr for ptr
stack[1]++;
// if underflows, gets garbage, don't underflow
return returnValue;
}
用于基准测试的示例内核:
__kernel void queue0(__global uint * heap)
{
int id=get_global_id(0);
__private uint q[100];
for(int i=0;i<256;i++)
q[i]=0;
for(int i=0;i<55;i++)
push(q,i);
for(int i=0;i<40;i++)
pop(q);
for(int i=0;i<20;i++)
push(q,i);
for(int i=0;i<35;i++)
pop(q);
for(int i=0;i<35;i++)
{
push(q,i);
pop(q);
}
push(q,'h');
push(q,'e');
push(q,'l');
push(q,'l');
push(q,'o');
push(q,' ');
push(q,'w');
push(q,'o');
push(q,'r');
push(q,'l');
push(q,'d');
for(int i=0;i<256;i++)
heap[id*256+i]=q[i];
}
缓冲区的输出(显示线程 id = 0 的计算结果)
121 110 0 0 0 0 0 0 0 0 0 0 // 121 pushes total, 110 pops total
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
104 101 108 108 111 32 119 111 114 108 100 0
// hello world
在 6.35 毫秒内超过 200k 的 pushes+pops(运行 1024 个线程的内核,每个线程处理 256 个元素,但仅使用 64+2 个元素作为循环缓冲区)对于 1 通道 1600MHz ddr3 RAM 和 Intel具有 12 个计算单元的 HD Graphics 400(总共 96 个内核 @600 MHz)。
如果您使用 64 x 4 元素循环缓冲区构造一个 64 元素循环缓冲区,您也可以在堆栈顶部和底部之间添加元素!
我有一些代码与此类似:
std::queue<unsigned> q;
q.push(array[0]);
while (!q.empty())
{
unsigned index = q.front();
q.pop();
if(index >= someval){
index = index - someval;
q.push(array[index]);
}
else{
//do something
}
}
}
我想将其移至 OpenCL 内核,我将如何以最有效的方式复制队列的功能。
目前我用一个固定大小的数组来实现它,它监视队列中元素的头部和计数。请问有没有更优雅的解决方案
谢谢
您应该检查 SYCL:https://www.khronos.org/sycl 其中 C++ 模板函数可以包含主机和设备代码以构建使用 OpenCL 加速的复杂算法。如果你不能使用它,如果你甚至没有 opencl 2.0 的设备:
1.2 版没有任何等效项。但是在评论中,你说:
Not sure that I understand what you mean but this would need to be individual to all threads on the GPU
那么就不需要线程间通信了,为了缓存+内存+计算效率,使用循环缓冲区实现一个FIFO应该很简单,只是不要溢出它(最多64个元素)并且不要't underflow(弹出次数多于推送次数,但易于实现边界检查(性能损失)):
推送
bool push(__private uint * stack, uint value)
{
// pushing from bot, so you can pop it from top later (FIFO)
// circular buffer for top performance
uint bufLen=64;
// zeroth element is counter for newest added element
// first element is oldest element
// circular buffer
uint nextIndex=(stack[0]%bufLen+2); // +2 because of top-bot headers
// if overflows, it overwrites oldest elements one by one
stack[nextIndex]=value;
// if overflows, it still increments
stack[0]++;
// simple and fast
return true;
}
检查是否为空
bool empty(__private uint * stack)
{
// tricky if you overflow both
return (stack[0]==stack[1]);
}
前值
uint front(__private uint * stack)
{
uint bufLen=64;
// oldest element value (top)
uint ptr=stack[1]%bufLen+2; // circular adr + 2 header
return stack[ptr];
}
爆裂
uint pop(__private uint * stack)
{
uint bufLen=64;
uint ptr=stack[1]%bufLen+2;
// pop from top (oldest)
uint returnValue=stack[ptr];
stack[ptr]=0;
// this will be new top ctr for ptr
stack[1]++;
// if underflows, gets garbage, don't underflow
return returnValue;
}
用于基准测试的示例内核:
__kernel void queue0(__global uint * heap)
{
int id=get_global_id(0);
__private uint q[100];
for(int i=0;i<256;i++)
q[i]=0;
for(int i=0;i<55;i++)
push(q,i);
for(int i=0;i<40;i++)
pop(q);
for(int i=0;i<20;i++)
push(q,i);
for(int i=0;i<35;i++)
pop(q);
for(int i=0;i<35;i++)
{
push(q,i);
pop(q);
}
push(q,'h');
push(q,'e');
push(q,'l');
push(q,'l');
push(q,'o');
push(q,' ');
push(q,'w');
push(q,'o');
push(q,'r');
push(q,'l');
push(q,'d');
for(int i=0;i<256;i++)
heap[id*256+i]=q[i];
}
缓冲区的输出(显示线程 id = 0 的计算结果)
121 110 0 0 0 0 0 0 0 0 0 0 // 121 pushes total, 110 pops total
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
104 101 108 108 111 32 119 111 114 108 100 0
// hello world
在 6.35 毫秒内超过 200k 的 pushes+pops(运行 1024 个线程的内核,每个线程处理 256 个元素,但仅使用 64+2 个元素作为循环缓冲区)对于 1 通道 1600MHz ddr3 RAM 和 Intel具有 12 个计算单元的 HD Graphics 400(总共 96 个内核 @600 MHz)。
如果您使用 64 x 4 元素循环缓冲区构造一个 64 元素循环缓冲区,您也可以在堆栈顶部和底部之间添加元素!