gpt4 book ai didi

cuda - 在 CUDA 中求解三对角线性系统

转载 作者:行者123 更新时间:2023-12-05 01:29:42 25 4
gpt4 key购买 nike

我正在尝试基于 Cyclic Reduction 实现三对角系统求解器我的方法 GTS450 .

循环减少在本文中进行了说明

Y. Zhang, J. Cohen, J.D. Owens, "Fast Tridiagonal Solvers on GPU"

但是,无论我做什么,我的 CUDA 代码都比顺序代码慢得多。我的结果一共是512 x 512点是7ms ,但是在我的 i7 3.4GHz 上它是 5ms . GPU 没有加速!

可能是什么问题?

#include "cutrid.cuh"
__global__ void cutrid_RC_1b(double *a,double *b,double *c,double *d,double *x)
{
int idx_global=blockIdx.x*blockDim.x+threadIdx.x;
int idx=threadIdx.x;

__shared__ double asub[512];
__shared__ double bsub[512];
__shared__ double csub[512];
__shared__ double dsub[512];

double at=0;
double bt=0;
double ct=0;
double dt=0;

asub[idx]=a[idx_global];
bsub[idx]=b[idx_global];
csub[idx]=c[idx_global];
dsub[idx]=d[idx_global];


for(int stride=1;stride<N;stride*=2)
{
int margin_left,margin_right;
margin_left=idx-stride;
margin_right=idx+stride;


at=(margin_left>=0)?(-csub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f;

bt=bsub[idx]+((margin_left>=0)?(-csub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f)
-((margin_right<512)?asub[idx+stride]*csub[idx]/bsub[idx+stride]:0.f);

ct=(margin_right<512)?(-csub[idx+stride]*asub[idx]/bsub[idx+stride]):0.f;

dt=dsub[idx]+((margin_left>=0)?(-dsub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f)
-((margin_right<512)?dsub[idx+stride]*csub[idx]/bsub[idx+stride]:0.f);

__syncthreads();
asub[idx]=at;
bsub[idx]=bt;
csub[idx]=ct;
dsub[idx]=dt;
__syncthreads();
}


x[idx_global]=dsub[idx]/bsub[idx];

}/*}}}*/

我通过 cutrid_RC_1b<<<512,512>>>(d_a,d_b,d_c,d_d,d_x) 启动了这个内核, 并达到 100%设备占用。这个结果让我困惑了好几天。

我的代码有一个改进版本:

    #include "cutrid.cuh"
__global__ void cutrid_RC_1b(float *a,float *b,float *c,float *d,float *x)
{/*{{{*/
int idx_global=blockIdx.x*blockDim.x+threadIdx.x;
int idx=threadIdx.x;

__shared__ float asub[512];
__shared__ float bsub[512];
__shared__ float csub[512];
__shared__ float dsub[512];

asub[idx]=a[idx_global];
bsub[idx]=b[idx_global];
csub[idx]=c[idx_global];
dsub[idx]=d[idx_global];
__syncthreads();
//Reduction
for(int stride=1;stride<512;stride*=2)
{
int margin_left=(idx-stride);
int margin_right=(idx+stride);
if(margin_left<0) margin_left=0;
if(margin_right>=512) margin_right=511;
float tmp1 = asub[idx] / bsub[margin_left];
float tmp2 = csub[idx] / bsub[margin_right];
float tmp3 = dsub[margin_right];
float tmp4 = dsub[margin_left];
__syncthreads();

dsub[idx] = dsub[idx] - tmp4*tmp1-tmp3*tmp2;
bsub[idx] = bsub[idx]-csub[margin_left]*tmp1-asub[margin_right]*tmp2;

tmp3 = -csub[margin_right];
tmp4 = -asub[margin_left];

__syncthreads();
asub[idx] = tmp3*tmp1;
csub[idx] = tmp4*tmp2;
__syncthreads();
}

x[idx_global]=dsub[idx]/bsub[idx];

}/*}}}*/

速度提高到0.73msQuadro k4000 上对于 512 x 512系统,但是上述论文中的代码在 0.5ms 中运行在 GTX280 上.

最佳答案

求解三对角方程组是一个具有挑战性的并行问题,因为经典求解方案(即高斯消元法)本质上是顺序的。

循环减少包括两个阶段:

  1. 前向缩减。原始系统分为两个独立的三对角系统,用于两组未知数,一组具有奇数索引,一组具有偶数索引。这样的系统可以独立求解,这一步可以看作是divide et impera 方案的第一步。两个较小的系统以相同的方式再次拆分为两个子系统,并重复该过程,直到系统只有 2。达到方程式。
  2. 向后替换2的系统先求解方程。然后,通过在不同内核上独立求解子系统,向上攀登divide et impera结构。

我不确定(但如果我错了请纠正我)您的代码是否会返回一致的结果。 N似乎没有定义。此外,您正在访问 csub[idx-stride] , 但我不确定 idx==0 是什么意思和 stride>1 .此外,您正在使用几个条件语句,主要用于边界检查。最后,您的代码缺少能够处理提到的 divide et impera 方案的适当线程结构,在概念上与 CUDA SDK 缩减示例中使用的方案非常相似。

正如我在上面的一条评论中提到的,我记得在 tridiagonalsolvers您可以找到用于求解三对角方程系统的循环缩减方案的实现。浏览相关的谷歌页面,在我看来,该代码由上述论文的第一作者(Yao Zhang)等人维护。代码复制并粘贴在下面。请注意,边界检查只进行一次(if (iRight >= systemSize) iRight = systemSize - 1;),因此限制了涉及的条件语句的数量。另请注意线程结构能够处理 divide et impera 方案。

Zhang、Cohen 和 Owens 的代码

__global__ void crKernel(T *d_a, T *d_b, T *d_c, T *d_d, T *d_x)
{
int thid = threadIdx.x;
int blid = blockIdx.x;

int stride = 1;

int numThreads = blockDim.x;
const unsigned int systemSize = blockDim.x * 2;

int iteration = (int)log2(T(systemSize/2));
#ifdef GPU_PRINTF
if (thid == 0 && blid == 0) printf("iteration = %d\n", iteration);
#endif

__syncthreads();

extern __shared__ char shared[];

T* a = (T*)shared;
T* b = (T*)&a[systemSize];
T* c = (T*)&b[systemSize];
T* d = (T*)&c[systemSize];
T* x = (T*)&d[systemSize];

a[thid] = d_a[thid + blid * systemSize];
a[thid + blockDim.x] = d_a[thid + blockDim.x + blid * systemSize];

b[thid] = d_b[thid + blid * systemSize];
b[thid + blockDim.x] = d_b[thid + blockDim.x + blid * systemSize];

c[thid] = d_c[thid + blid * systemSize];
c[thid + blockDim.x] = d_c[thid + blockDim.x + blid * systemSize];

d[thid] = d_d[thid + blid * systemSize];
d[thid + blockDim.x] = d_d[thid + blockDim.x + blid * systemSize];

__syncthreads();

//forward elimination
for (int j = 0; j <iteration; j++)
{
__syncthreads();
stride *= 2;
int delta = stride/2;

if (threadIdx.x < numThreads)
{
int i = stride * threadIdx.x + stride - 1;
int iLeft = i - delta;
int iRight = i + delta;
if (iRight >= systemSize) iRight = systemSize - 1;
T tmp1 = a[i] / b[iLeft];
T tmp2 = c[i] / b[iRight];
b[i] = b[i] - c[iLeft] * tmp1 - a[iRight] * tmp2;
d[i] = d[i] - d[iLeft] * tmp1 - d[iRight] * tmp2;
a[i] = -a[iLeft] * tmp1;
c[i] = -c[iRight] * tmp2;
}
numThreads /= 2;
}

if (thid < 2)
{
int addr1 = stride - 1;
int addr2 = 2 * stride - 1;
T tmp3 = b[addr2]*b[addr1]-c[addr1]*a[addr2];
x[addr1] = (b[addr2]*d[addr1]-c[addr1]*d[addr2])/tmp3;
x[addr2] = (d[addr2]*b[addr1]-d[addr1]*a[addr2])/tmp3;
}

// backward substitution
numThreads = 2;
for (int j = 0; j <iteration; j++)
{
int delta = stride/2;
__syncthreads();
if (thid < numThreads)
{
int i = stride * thid + stride/2 - 1;
if(i == delta - 1)
x[i] = (d[i] - c[i]*x[i+delta])/b[i];
else
x[i] = (d[i] - a[i]*x[i-delta] - c[i]*x[i+delta])/b[i];
}
stride /= 2;
numThreads *= 2;
}

__syncthreads();

d_x[thid + blid * systemSize] = x[thid];
d_x[thid + blockDim.x + blid * systemSize] = x[thid + blockDim.x];

关于cuda - 在 CUDA 中求解三对角线性系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19541620/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com