C 数值积分-如何并行化?

C 数值积分-如何并行化?,c,algorithm,opencl,parallel-processing,C,Algorithm,Opencl,Parallel Processing,我从OpenCL开始,我可以看到添加向量的例子并理解它。但我在考虑梯形法。这是[a,b]中x^2积分计算的代码(C) double f(double x) { return x*x; } double Simple_Trap(double a, double b) { double fA, fB; fA = f(a); fB = f(b); return ((fA + fB) * (b-a)) / 2; } double Comp_Trap( doub

我从OpenCL开始,我可以看到添加向量的例子并理解它。但我在考虑梯形法。这是[a,b]中x^2积分计算的代码(C)

double f(double x)
{
    return x*x;
}

double Simple_Trap(double a, double b)
{
    double fA, fB;
    fA = f(a);
    fB = f(b);
    return ((fA + fB) * (b-a)) / 2;
}

double Comp_Trap( double a, double b)
{
    double Suma = 0;
    double i = 0;
    i = a + INC;
    Suma += Simple_Trap(a,i);
    while(i < b)
    {
        i+=INC;
        Suma += Simple_Trap(i,i + INC);
    }
    return Suma;
}
双f(双x)
{
返回x*x;
}
双简单_陷阱(双a,双b)
{
双fA,fB;
fA=f(a);
fB=f(b);
返回((fA+fB)*(b-a))/2;
}
双补偿陷阱(双a、双b)
{
双Suma=0;
双i=0;
i=a+INC;
Suma+=简单_陷阱(a,i);
而(i
问题是?如何使用梯形法获得积分计算的核


所以,我在考虑这个想法:partials[I]=积分(a,a+偏移量),然后制作一个内核来计算partials之和,如Patrick87所述


但是,这是最好的方法吗?

梯形法只是对黎曼和的一点改进。要并行执行此操作,您需要将间隔分解为您希望有线程的任意多个子间隔;然后,让每个线程在其子间隔上集成函数。最后,对前一阶段计算的所有积分进行全局和归约。您可以尝试每个阶段使用多少线程。

以下是我的想法。我没有对这个内核进行端到端测试。当我有更多的时间时,我会做一个更新

comp_trap是基于上述代码的基本分治方法。 comp_trap_multi通过让每个工作项划分其子部分来提高准确性

您只需要在主机中分配一个double数组,以便每个工作组有一个double来返回其结果。这将有助于减少您想要避免的向量分配

如果有任何问题,请告诉我

更新:

1) 将所有双精度引用更改为float,因为在opencl中双精度是可选的

2) 将工作组大小硬编码为64。这个值在我的系统上是最优的,应该通过实验确定。我更喜欢硬编码这个值,而不是传入要使用的本地浮点数组,因为宿主程序最终只会在目标系统上使用最佳值

3) 修正了一个错误的计算(a1是错误的,现在应该更好了)

/*
数值积分
*/
浮点数f(浮点数x)
{
返回x*x;
}
浮子简易隔水弯(浮子a、浮子b)
{
浮动fA,fB;
fA=f(a);
fB=f(b);
返回((fA+fB)*(b-a))/2;
}
__内核无效comp_陷阱(
浮动a,
浮球b,
__全球浮动*总和)
{
/*
-假设1D全局和局部工作维度
-每个工作单元将计算总金额的1/get_global_大小
-然后,每个组的第0个工作单元为
分组并将其存储在u全局*总和中
-内存分配:sizeof(sums)=get_num_groups(0)*sizeof(float)
-假设本地草稿行大小至少为组中每个工作单元8字节
ie sizeof(wiSums)=获取本地大小(0)*sizeof(浮点)
*/
__本地浮动wiSums[64];
int l_id=get_local_id(0);
//本工作项的计算范围为:a1、b1
float a1=a+((b-a)/get_global_size(0))*get_global_id(0);
浮点b1=a1+(b-a)/获取全局大小(0);
wiSums[l_id]=简单_陷阱(a1,b1);
屏障(CLK_本地_MEM_围栏);
int i;
如果(l_id==0){
对于(i=1;i 0){
范围=(b1-a1)/分区;
}否则{
范围=(b1-a1);
}
int i;
wiSums[l_id]=0;

对于(i=0;iYes我理解这个概念,但如何实现它?因为我正在阅读OpenCl编程指南的前几章,但它适用于并行数据结构,而线程的概念没有被提及。这是因为OpenCl没有线程概念,等效的概念是WorkItemsMMM是的,我想我应该为?@fac做一个并行undo.hpc:如果您知道如何在OpenCL中进行向量加法,只需将计算r[i]=a[i]+b[i]的部分替换为r[i]=Integrate(i,i+1),其中Integrate只是函数在区间[i,i]上的串行积分。然后编写一个内核(或在CPU端)来计算所有r[i]的总和.我考虑过,但这是最好的方法?我不想用向量来存储部分积分。不过,我会试试。
/*
numerical-integration.cl
*/

float f(float x)
{
    return x*x;
}

float simple_trap(float a, float b)
{
    float fA, fB;
    fA = f(a);
    fB = f(b);
    return ((fA + fB) * (b-a)) / 2;
}

__kernel void comp_trap(
    float a,
    float b,
    __global float* sums)
{
/*
- assumes 1D global and local work dimensions
- each work unit will calculate 1/get_global_size of the total sum
- the 0th work unit of each group then accumulates the sum for the
group and stores it in __global * sums
- memory allocation: sizeof(sums) = get_num_groups(0) * sizeof(float)
- assumes local scratchpad size is at lease 8 bytes per work unit in the group
ie sizeof(wiSums) = get_local_size(0) * sizeof(float)
*/
    __local float wiSums[64];
    int l_id = get_local_id(0);

    //cumpute range for this work item is: a1, b1 
    float a1 = a+((b-a)/get_global_size(0))*get_global_id(0);
    float b1 = a1+(b-a)/get_global_size(0);

    wiSums[l_id] = simple_trap(a1,b1);

    barrier(CLK_LOCAL_MEM_FENCE);

    int i;
    if(l_id == 0){
        for(i=1;i<get_local_size(0);i++){
            wiSums[0] += wiSums[i];
        }
        sums[get_group_id(0)] = wiSums[0];
    }
}

__kernel void comp_trap_multi(
    float a,
    float b,
    __global float* sums,
    int divisions)
{
/*
- same as above, but each work unit further divides its range into
'divisions' equal parts, yielding a more accurate result
- work units still store only one sum in the local array, which is
used later for the final group accumulation
*/
    __local float wiSums[64];
    int l_id = get_local_id(0);

    float a1 = a+((b-a)/get_global_size(0))*get_global_id(0);
    float b1 = a1+(b-a)/get_global_size(0);
    float range;
    if(divisions > 0){
        range = (b1-a1)/divisions;
    }else{
        range = (b1-a1);
    }

    int i;
    wiSums[l_id] = 0;
    for(i=0;i<divisions;i++){
        wiSums[l_id] += simple_trap(a1+range*i,a1+range*(i+1));
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if(l_id == 0){
        for(i=1;i<get_local_size(0);i++){
            wiSums[0] += wiSums[i];
        }
        sums[get_group_id(0)] = wiSums[0];
    }
}