OpenCL中二进制幂运算的快速实现

OpenCL中二进制幂运算的快速实现,c,opencl,pow,exponentiation,modular-arithmetic,C,Opencl,Pow,Exponentiation,Modular Arithmetic,我一直在尝试在OpenCL中设计一个快速的二进制求幂实现。我当前的实现与中的实现非常相似 //返回16^n mod ak 内联双扩展(长n,双ak) { 双r=16.0; 长新界; 如果(ak==1)返回0。; 如果(n==0)返回1; 如果(n==1)返回fmod(16.0,ak); 对于(nt=1;nt>=1; }而(nt!=0); 返回r; } 还有改进的余地吗?现在我的程序大部分时间都花在这个功能上。 提取nt=log2(n);的循环可以替换为 如果(n&1)…;n>=1; 在do-w

我一直在尝试在OpenCL中设计一个快速的二进制求幂实现。我当前的实现与中的实现非常相似

//返回16^n mod ak
内联双扩展(长n,双ak)
{
双r=16.0;
长新界;
如果(ak==1)返回0。;
如果(n==0)返回1;
如果(n==1)返回fmod(16.0,ak);
对于(nt=1;nt>=1;
}而(nt!=0);
返回r;
}
还有改进的余地吗?现在我的程序大部分时间都花在这个功能上。

  • 提取
    nt=log2(n);
    的循环可以替换为
    如果(n&1)…;n>=1;

    在do-while循环中
  • 考虑到最初的
    r=16;
    ,fmod(r*r,ak)与fmod(16*r,ak)可以很容易地延迟,以便仅在大约每n次迭代时计算模——循环展开
  • 为什么是fmod

我的第一个想法是将其矢量化,潜在速度约为1.6倍。与原始循环中的2倍相比,每个循环使用5倍,但对于足够大的N,循环数量约为四分之一。将所有的
转换为
s,并将
fmod
s替换为
%
s可能会提供一些加速,具体取决于所使用的GPU以及其他什么

inline double expm(long n, double ak) {

    double4 r = (1.0, 1.0, 1.0, 1.0);
    long4 ns = n & (0x1111111111111111, 0x2222222222222222, 0x4444444444444444,
            0x8888888888888888);
    long nt;

    if(ak == 1) return 0.;

    for(nt=15; nt<n; nt<<=4); //This can probably be vectorized somehow as well.

    do {
        double4 tmp = r*r;
        tmp = tmp*tmp;
        tmp = tmp*tmp;
        r = fmod(tmp*tmp, ak); //Raise it to the 16th power, 
                                       //same as multiplying the exponent 
                                       //(of the result) by 16, same as
                                       //bitshifting the exponent to the right 4 bits.

        r = select(fmod(r*(16.0,256.0,65536.0, 4294967296.0), ak), r, (ns & nt) - 1);
        nt >>= 4;
    } while(nt != 0); //Process n four bits at a time.

    return fmod(r.x*r.y*r.z*r.w, ak); //And then combine all of them.
}
inlinedouble-expm(长n,双ak){
Double4R=(1.0,1.0,1.0,1.0);
长4 ns=n&(0x1111111111111,0x2222222,0x444444,
0x8888888888);
长新界;
如果(ak==1)返回0。;

对于(nt=15;n任何关于输入的一般范围的想法?n的范围可以达到一百万左右。因此,相当大的输入范围。
ak
实际上是双精度还是整数?它的范围是什么?很抱歉延迟-我现在回来了。ak(据我所知)是整数。出于某种原因,此实现将其视为双精度(也许是为了避免在某个时候施放?)。实际上,我还没有用ak作为int而不是double来测试整个程序,所以我不知道在后面的部分中进行这样的切换是否会降低其准确性或影响性能。这看起来很棒!不幸的是,我今天没有太多时间来弄清楚您的代码到底在做什么;我可能明天会看一看。谢谢!我想我现在可以使用它了。它的要点是它使用向量类型一次处理4位输入。我今天将测试它。谢谢!嗯……它似乎工作不正常。例如,在第一个工作项的第一次迭代中,它必须计算expm(6,9)(换句话说,16^6%9)。expm()当答案为1时返回9。看起来我向后设置了select,但在return语句中忘记了fmod,尽管我不知道这两种情况是如何导致该错误的。是否要报告其他损坏的情况?
inline double expm(long n, double ak) {

    double4 r = (1.0, 1.0, 1.0, 1.0);
    long4 ns = n & (0x1111111111111111, 0x2222222222222222, 0x4444444444444444,
            0x8888888888888888);
    long nt;

    if(ak == 1) return 0.;

    for(nt=15; nt<n; nt<<=4); //This can probably be vectorized somehow as well.

    do {
        double4 tmp = r*r;
        tmp = tmp*tmp;
        tmp = tmp*tmp;
        r = fmod(tmp*tmp, ak); //Raise it to the 16th power, 
                                       //same as multiplying the exponent 
                                       //(of the result) by 16, same as
                                       //bitshifting the exponent to the right 4 bits.

        r = select(fmod(r*(16.0,256.0,65536.0, 4294967296.0), ak), r, (ns & nt) - 1);
        nt >>= 4;
    } while(nt != 0); //Process n four bits at a time.

    return fmod(r.x*r.y*r.z*r.w, ak); //And then combine all of them.
}