OpenCL-计算期间的增量求和
我绝对是OpenCL编程的新手。对于我的应用程序。(分子模拟)我写了一个内核来计算lennard-jones液体的分子间势。在这个内核中,我需要计算所有粒子势的累积值,其中一个:OpenCL-计算期间的增量求和,opencl,Opencl,我绝对是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 floa
__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;
}
}
\uuuuu内核无效Molsim(\uuuu全局常量浮点*inmatrix,\uuuu全局浮点*fi,常量int c,常量浮点r1,常量浮点r2,常量浮点r3,常量浮点rc,常量浮点ε,常量浮点sigma,常量浮点h1,常量浮点h23)
{
浮动fi0;
浮动fi1;
浮动d;
unsigned int i=get_global_id(0);//粒子数(通常为2000)
如果(c!=i){
//粒子运动前的势
d=sqrt(pow((0.5*h1晶圆厂(0.5*h1晶圆厂(inmatrix[c*3]-inmatrix[i*3])),2.0)+pow((0.5*h23晶圆厂(0.5*h23晶圆厂(inmatrix[c*3+1]-inmatrix[i*3+1])),2.0)+pow((0.5*h23晶圆厂(inmatrix[c*3+2]-inmatrix[i*3+2])),2.0);
如果(d我认为你需要为fi[0]+=fi1-fi0添加原子函数
警告:使用原子函数会降低性能
这里有两个增量原子函数的例子
没有原子函数和两个工作项的示例:
__kernel void inc(global int * num){
num[0]++; //num[0] = 0
}
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
__kernel void inc(global int * num){
atom_inc(&num[0]);
}
工作项1读取num[0]:0
工作项2读取num[0]:0
工作项1增加num[0]:0+1
工作项2递增num[0]:0+1
工作项1写入num[0]:num[0]=1
工作项2写入num[0]:num[0]=1
结果:num[0]=1
具有原子函数和2个工作项的示例:
__kernel void inc(global int * num){
num[0]++; //num[0] = 0
}
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
__kernel void inc(global int * num){
atom_inc(&num[0]);
}
工作项1读取num[0]:0
工作项1增加num[0]:0+1
工作项1写入num[0]:num[0]=1
工作项2读取num[0]:1
工作项2增加数值[0]:1+1
工作项2写入num[0]:num[0]=2
结果:num[0]=2原子添加是一种解决方案,但您可能会遇到性能问题,因为原子部分将序列化您的工作项
我认为更好的解决方案是,对于每个工作项,在它们自己的变量中写入,如:
fi[get_global_id(0)]+=fi1-fi0
然后,您可以将数组传输到CPU并对所有元素求和,也可以在GPU上使用算法并行执行。所有线程都由“组”执行。您可以使用get_local_id(dim)函数确定组中的线程id。每个组中的线程都可以使用共享内存(称为“本地内存”)在OpenCL中)和同步它们的执行,但不同组中的线程不能直接通信
因此,还原的典型解决方案如下:
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(全局)和tmp_reduce(本地)添加到内核参数:
__kernel void Molsim(..., __global float *part_sum, __local float *tmp_reduce)
分配大小等于内核组数(=全局大小/局部大小)的浮点数组,并设置参数part\u sum
将参数tmp_reduce设置为内核“local size”x size_的(float)和NULL:
clSetKernelArg(kernel,<par number>,sizeof(float)*<local_size>,NULL);
clSetKernelArg(内核,sizeof(float)*,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;
}
}
int loc\u id=get\u local\u id(0);
...
//fi[0]+=fi1-fi0;
tmp_reduce[loc_id]=fi1-fi0;
}
屏障(CLK_本地_MEM_围栏);
如果(loc_id==0){
int i;
浮动s=tmp_减少[0];
对于(i=1;我排除了iAtomic函数,因为fi必须是浮点变量是的,这是解决方案。但由于主机应用程序中的序列化求和,将降低性能。此外,将整个数组存储回CPU内存将比存储一个变量慢。在这一行中,它应该是:float s=tmp\u reduce[0]?但每次运行后,我都会得到不同的结果。这里可能需要求和归约,否则,对于GPU来说,原子或串行求和会很糟糕。这有点难掌握,但相对容易实现(特别是当要求和的元素数是二的幂时)考虑私有内存中的缓存值:<代码> in MatL[I* 3 + 0/1 / 2 ] < /代码>,因为您不止一次使用它。对于和,只使用一个约简算法。