使用OpenACC正确实现稀疏矩阵的三角求解器

时间:2019-01-23 17:58:13

标签: solver openacc triangular

我目前正在为稀疏矩阵实现三角求解器,并且我尝试使用OpenACC指令进行加速。给定我的矩阵因子LU为稀疏CSR格式,OpenACC设法正确地解决了L因子,但是与应用程序的真实解决方案相比,U因子完全错误。这是用于向后替换任务的加速内核的代码:

#pragma acc kernels deviceptr( ia, ja, factorValsU, y, x )
{
    for ( int idx = size; idx > 0; idx-- )
    {
        double temp = 0.0;
        int rowInit = ia[ idx - 1];
        int rowEnd  = ia[ idx ];
        #pragma acc loop vector reduction( + : temp)
        for ( int k = rowInit + 1; k < rowEnd; k++ )
        {
            temp += factorValsU[ k ] * x[ ja[ k ] ];
        }
        x[ idx ] = (y[ idx ] - temp) / factorValsU[ rowInit ];
    }
 }

我对这个内核为什么会产生错误结果一无所知。我已经为内核尝试了一个不同的版本,其中矩阵是从后向下存储的,即从下到上,原则上可以通过以下内核解决:

#pragma acc kernels deviceptr( ia, ja, factorValsU, y, x )
{
    for ( int idx = 0; idx < size; idx++ )
    {
        double temp = 0.0;
        int rowInit = ia[ idx ];
        int rowEnd  = ia[ idx + 1 ];
        #pragma acc loop vector reduction( + : temp)
        for ( int k = rowInit + 1; k < rowEnd; k++ )
        {
            temp += factorValsU[ k ] * x[ ja[ k ] ];
        }
        x[ size - idx ] = (y[ size - idx ] - temp) / factorValsU[ rowInit ];
    }
 }

但是结果总是错误的。我是否错过了一些有关使用OpenACC指令装饰常规代码以获得适当结果的基本知识?

如前所述,L因子的正向替换正常工作,因此,出于完整性考虑,我确实将代码发布在这里。

#pragma acc kernels deviceptr( ia, ja, factorValsL, y, x )
{
    for ( int idx = 0; idx < size; idx++ )
    {
        double temp = 0.0;
        int rowInit = ia[ idx ];
        int rowEnd  = ia[ idx + 1 ];
        #pragma acc loop vector reduction( + : temp)
        for ( int k = rowInit; k < rowEnd; k++ )
        {
            temp += factorValsL[ k ] * x[ ja[ k ] ];
        }
        x[ idx ] = y[ idx ] - temp;
    }
 }

请注意,内核在进行正向替换(有效)和向后替换(均不起作用)之间的细微差别是保存结果的内存区域:

x[ idx ] = y[ idx ] - temp     for the L factor 
x[ size - idx ] = (y[ size - idx ] - temp) / factorValsU[ rowInit ] for the U factor;

U因子求解器是否有某种原因计算错误的结果,从而导致在内存中进行分配(和讲课)的顺序?

为完整起见,pgi18.4编译器提供的有关内核的信息为:

triangularSolverU_acc(int, const int *, const int *, const double *, const double *, double *, bool):
614, Complex loop carried dependence of y->,x->,factorVals-> prevents parallelization
     Loop carried dependence of x-> prevents parallelization
     Loop carried backward dependence of x-> prevents vectorization
     Accelerator kernel generated
     Generating Tesla code
    614, #pragma acc loop seq
    621, #pragma acc loop vector(128) /* threadIdx.x */
         Generating reduction(+:temp)
621, Loop is parallelizable

这表明外部循环已被序列化,内部循环已简化。

1 个答案:

答案 0 :(得分:0)

使用“内核”,编译器必须证明该循环不包含任何依赖关系,因此可以安全地进行并行化。但是,由于您的代码包含指针,并且这些指针可能被别名为相同的内存,因此编译器无法证明这一点,因此安全起见,并使循环按顺序运行。要覆盖编译器分析,可以在外部for循环之前添加“独立于#pragma acc循环”。 “独立”是对编译器的断言:该循环可以安全地并行化。另外,您可以使用“并行循环”指令代替“内核”,因为“并行”表示“独立”。

对于错误答案,通常是由于主机和设备副本之间的数据未正确同步。您如何管理数据移动?由于您正在使用“ deviceptr”,因此这意味着您正在使用CUDA。

此外,如果您可以发布完整的复制示例,则可以更轻松地帮助确定问题。