我目前正在为稀疏矩阵实现三角求解器,并且我尝试使用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
这表明外部循环已被序列化,内部循环已简化。
答案 0 :(得分:0)
使用“内核”,编译器必须证明该循环不包含任何依赖关系,因此可以安全地进行并行化。但是,由于您的代码包含指针,并且这些指针可能被别名为相同的内存,因此编译器无法证明这一点,因此安全起见,并使循环按顺序运行。要覆盖编译器分析,可以在外部for循环之前添加“独立于#pragma acc循环”。 “独立”是对编译器的断言:该循环可以安全地并行化。另外,您可以使用“并行循环”指令代替“内核”,因为“并行”表示“独立”。
对于错误答案,通常是由于主机和设备副本之间的数据未正确同步。您如何管理数据移动?由于您正在使用“ deviceptr”,因此这意味着您正在使用CUDA。
此外,如果您可以发布完整的复制示例,则可以更轻松地帮助确定问题。