【问题标题】:OpenCL pass by reference different addres spaceOpenCL通过引用不同的地址空间传递
【发布时间】:2013-02-02 12:42:45
【问题描述】:

短篇小说:

我有输出变量的引用传递函数

void acum( float2 dW, float4 dFE, float2 *W, float4 *FE )

如果满足某些条件,则假定变量 *W、*FE、dW、dFE 递增。 我想让这个函数通用——输出变量可以是本地的也可以是全局的。

acum( dW, dFE, &W , &FE  );   // __local acum
acum( W, FE, &Wout[idOut], &FEout[idOut] );  // __global acum

当我尝试编译它时,我得到了错误

error: illegal implicit conversion between two pointers with different address spaces

有可能以某种方式实现吗???我在想是否可以使用宏而不是函数(但我对 C 中的宏不是很熟悉)。

另一种可能是:

  1. 使用返回结构的函数{Wnew, FEnew}
  2. 或 (float8)(Wnew,FEnew,0,0) 但我不喜欢它,因为它会使代码更加混乱。
  3. 当然,我也不想在所有地方都复制“acum”的主体(比如手动内联它:))

背景(不需要阅读):

我正在尝试使用 OpenCL 对一些热力学采样进行编程。因为统计权重 W = exp(-E/kT) 很容易在低温下溢出浮点数 (2^64),所以我制作了一个组合数据类型 W = float2(W_digits, W_exponent) 并定义了操作这些数字“acum”的函数。

我尝试最小化全局内存操作的数量,所以我让 work_items 超过 Vsurf 而不是 FEout,因为我预计 Vsurf 中只有几个点会有相当大的统计权重,所以 FEout 的累积只会被调用几次每个工作项。虽然迭代 FEout 需要 sizeof(FEout)*sizeof(Vsurf) 内存操作。 完整的代码在这里(欢迎任何如何使它更有效的建议):

// ===== function :: FF_vdW  - Lenard-Jones Van Der Waals forcefield
float4 FF_vdW ( float3 R ){
 const float C6    = 1.0;
 const float C12   = 1.0;
 float ir2  = 1.0/ dot( R, R );
 float ir6  = ir2*ir2*ir2;
 float ir12 = ir6*ir6;
 float E6   = C6*ir6;
 float E12  = C12*ir12;
 return (float4)(     
                ( 6*E6  -  12*E12 ) * ir2    *  R 
                ,   E12 -     E6
);}

// ===== function :: FF_spring  - harmonic forcefield
float4 FF_spring( float3 R){
 const float3 k = (float3)(  1.0, 1.0, 1.0 );
 float3 F = k*R;
 return (float4)(  F,
                   0.5*dot(F,R)
);}

// ===== function :: EtoW    -   compute statistical weight
float2 EtoW( float EkT ){
    float Wexp = floor( EkT);
    return (float2)(  exp(EkT - Wexp)
                   ,   Wexp
); }

// ===== procedure : addExpInplace    -- acumulate F,E with statistical weight dW
void acum( float2 dW, float4 dFE, float2 *W, float4 *FE   )
{
            float dExp = dW.y - (*W).y; // log(dW)-log(W)
            if(dExp>-22){               // e^22 = 2^32 ,    single_float = 2^+64
               float fac = exp(dExp); 
               if (dExp<0){             // log(dW)<log(W)
                 dW.x *= fac;
                 (*FE)    += dFE*dW.x;
                 (*W ).x  +=     dW.x;
               }else{                   // log(dW)>log(W)
                 (*FE)     = dFE  + fac*(*FE); 
                 (*W ).x   = dW.x + fac*(*W).x;
                 (*W ).y   = dW.y;
               }
            }
}

// ===== __kernel :: sampler
__kernel void sampler( 
__global float  * Vsurf, // in  : surface potential (including vdW)  // may be faster to precomputed endpoints positions like float8
__global float4 * FEout, // out : Fx,Fy,Fy, E
__global float2 * Wout,  // out : W_digits, W_exponent
int3   nV    ,
float3 dV    ,
int3   nOut     ,
int3   iOut0    ,        // shift of Fout in respect to Vsurf
int3   nCopy    ,        // number of copies of 
int3   nSample  ,        // dimension of sampling in each dimension around R0   +nSample,-nSample
float3 RXe0     ,        // postion Xe relative to Tip
float  EcutSurf ,        
float  EcutTip  ,        
float  logWcut  ,        // accumulate only when  log(W) > logWcut
float  kT                // maximal energy which should be sampled
) {

    int  id  = get_global_id(0);  // loop over potential grid points
    int  idx = id/nV.x; 
    int3 iV  =  (int3)( idx/nV.y
                      , idx%nV.y
                      , id %nV.x  );
    float V = Vsurf[id];
    float3 RXe = dV*iV;

if (V<EcutSurf){
// loop over tip position
for (int iz=0;iz<nOut.z;iz++ ){
for (int iy=0;iy<nOut.y;iy++ ){
for (int ix=0;ix<nOut.x;ix++ ){ 

    int3   iTip = (int3)( iz, iy, ix );
    float3 Rtip = dV*iTip;
    float4 FE = 0;
    float2 W  = 0;
    // loop over images of potential
    for (int ix=0;ix<nCopy.x;ix++ ){
    for (int iy=0;iy<nCopy.y;iy++ ){
        float3 dR = RXe - Rtip;
        float4 dFE    = FF_vdW( dR );
               dFE   += FF_spring( dR - RXe0 );
               dFE.w += V;

        if( dFE.w < EcutTip  ){
            float2 dW = EtoW( - FE.w / kT );
            acum( dW, dFE, &W , &FE  );   // __local acum
        }
    } 
    }

    if( W.y > logWcut  ){ // accumulate force
        int  idOut = iOut0.x + iOut0.y*nOut.x + iOut0.z*nOut.x*nOut.y;
        acum( W, FE, &Wout[idOut], &FEout[idOut] );  // __global acum
    }

}}}}

}

我在 ubuntu 12.04 64bit 上使用 pyOpenCL,但我认为这与问题无关

【问题讨论】:

    标签: opencl pass-by-reference


    【解决方案1】:

    好的,这里发生了什么,来自 OpenCL 手册页:

    http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/global.html

    "如果对象的类型由地址空间名称限定,则在指定的地址名称中分配对象;否则,在通用地址空间中分配对象"

    ...

    “程序中函数参数或函数局部变量的通用地址空间名称是__private。所有函数参数都应在__private地址空间中。”

    所以您的 acum(... ) 函数参数位于 __private 地址空间中。

    所以编译器是正确的说

    acum(..&Wout[idOut], &FEout[idOut])

    当函数 args 必须在私有地址空间中时,在全局地址空间中使用 &Wout 和 &FEout 调用。

    解决方案是全局和私有之间的转换。

    创建两个私有临时变量来接收结果。

    使用这些变量调用 acum( ... )。

    在调用 acum(..) 之后,将临时私有值分配给全局值

    代码看起来有点乱

    请记住,在 GPU 上,您有许多地址空间,您不能通过强制转换来神奇地在它们之间跳转。您必须通过赋值在地址空间之间显式移动数据。

    【讨论】:

    • 您还可以将全局限定符添加到必要的 accum(...) 函数参数中。但是你最终会得到两个函数,一个有全局函数,一个没有。而且您需要不同的名称,因为 ISO C 99 中没有函数名称重载。
    猜你喜欢
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 2020-12-29
    • 1970-01-01
    • 2015-06-20
    相关资源
    最近更新 更多