我绝对是OpenCL编程的新手。对于我的应用程序(分子模拟)我写了一个用于计算lennard-jones液体分子间势的核。在这个内核中,我需要用一个计算所有粒子的潜在累积值:
__kernel void Molsim(__global const float* inmatrix, __global float* fi, const int c, const float r1, const float r2, const float r3, const float rc, const float epsilon, const float sigma, const float h1, const float h23)
{
float fi0;
float fi1;
float d;
unsigned int i = get_global_id(0); //number of particles (typically 2000)
if(c!=i) {
// potential before particle movement
d=sqrt(pow((0.5*h1-fabs(0.5*h1-fabs(inmatrix[c*3]-inmatrix[i*3]))),2.0)+pow((0.5*h23-fabs(0.5*h23-fabs(inmatrix[c*3+1]-inmatrix[i*3+1]))),2.0)+pow((0.5*h23-fabs(0.5*h23-fabs(inmatrix[c*3+2]-inmatrix[i*3+2]))),2.0));
if(d<rc) {
fi0=4.0*epsilon*(pow(sigma/d,12.0)-pow(sigma/d,6.0));
}
else {
fi0=0;
}
// potential after particle movement
d=sqrt(pow((0.5*h1-fabs(0.5*h1-fabs(r1-inmatrix[i*3]))),2.0)+pow((0.5*h23-fabs(0.5*h23-fabs(r2-inmatrix[i*3+1]))),2.0)+pow((0.5*h23-fabs(0.5*h23-fabs(r3-inmatrix[i*3+2]))),2.0));
if(d<rc) {
fi1=4.0*epsilon*(pow(sigma/d,12.0)-pow(sigma/d,6.0));
}
else {
fi1=0;
}
// cumulative difference of potentials
// fi[0]+=fi1-fi0; changed to full size vector
fi[get_global_id(0)]=fi1-fi0;
}
}
我的问题在于:fi [0] + = fi1-fi0 ;.在单元素向量中,fi [0]是错误的结果。 我读了一些关于总和减少的东西,但我不知道在计算过程中该怎么做。
存在我的问题的任何简单解决方案?
通知: 我尝试为向量组件的总和添加下一个内核(参见下面的代码),但是比使用CPU对向量求和时有更大的减速。
__kernel void Arrsum(__global const float* inmatrix, __global float* outsum, const int inmatrixsize, __local float* resultScratch)
{
// načtení indexu
int gid = get_global_id(0);
int wid = get_local_id(0);
int wsize = get_local_size(0);
int grid = get_group_id(0);
int grcount = get_num_groups(0);
int i;
int workAmount = inmatrixsize/grcount;
int startOffest = workAmount * grid + wid;
int maxOffest = workAmount * (grid + 1);
if(maxOffest > inmatrixsize){
maxOffest = inmatrixsize;
}
resultScratch[wid] = 0.0;
for(i=startOffest;i<maxOffest;i+=wsize){
resultScratch[wid] += inmatrix[i];
}
barrier(CLK_LOCAL_MEM_FENCE);
if(gid == 0){
for(i=1;i<wsize;i++){
resultScratch[0] += resultScratch[i];
}
outsum[grid] = resultScratch[0];
}
}
答案 0 :(得分:2)
我认为你需要fi [0] + = fi1-fi0的atomic_add原子函数;
警告:使用原子功能会降低性能。
这里有两个带增量原子函数的例子。
没有原子功能和2个工作项的示例:
__kernel void inc(global int * num){
num[0]++; //num[0] = 0
}
结果:num [0] = 1
原子功能和2个工作项的示例:
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
__kernel void inc(global int * num){
atom_inc(&num[0]);
}
结果:num [0] = 2
答案 1 :(得分:0)
原子添加是一种解决方案,但您可能会遇到性能问题,因为原子部件会序列化您的工作项。
我认为,对于每个工作项,更好的解决方案是在自己的变量中编写,例如:
fi [get_global_id(0)] + = fi1-fi0;
然后你可以将数组传输到CPU并对所有元素求和,或者你在GPU上用算法并行执行它。
答案 2 :(得分:0)
所有线程都由“组”执行。您可以使用get_local_id(dim)函数确定组中的线程ID。每个组中的线程可以使用共享内存(在OpenCL中称为“本地内存”)并同步它们的执行,但不同组中的线程无法直接通信。
因此,减少的典型解决方案如下:
将临时数组part_sum(global)和tmp_reduce(local)添加到内核args:
__kernel void Molsim(..., __global float *part_sum, __local float *tmp_reduce)
分配大小等于内核组数(= global_size / local_size)的浮点数组,并设置参数part_sum。
使用内核“local size”x size_of(float)和NULL设置参数tmp_reduce:
clSetKernelArg(kernel,<par number>,sizeof(float)*<local_size>,NULL);
在内核中,将代码替换为:
int loc_id=get_local_id(0);
...
// fi[0]+=fi1-fi0;
tmp_reduce[loc_id]=fi1-fi0;
}
barrier(CLK_LOCAL_MEM_FENCE);
if(loc_id==0) {
int i;
float s=tmp_reduce[0];
for(i=1;i<get_local_size(0);i++)
s+=tmp_reduce[i];
part_sum[get_group_id(0)]=s;
}
}
完成内核执行后,只需在主机上总结part_sum [array]的内容,它比global_size小得多。
这不是完全“并行缩减”,因为您可以使用更复杂的算法使用Log2(local_size)操作并行地对tmp_reduce数组求和,但这必须比原子操作快得多。
另外,请查看http://developer.amd.com/Resources/documentation/articles/pages/OpenCL-Optimization-Case-Study-Simple-Reductions_2.aspx的beter parallel reduction方法。