【发布时间】:2016-10-05 17:31:45
【问题描述】:
我已经根据《数值食谱》一书中描述的例程实现了 Jacobi 算法,但由于我计划处理非常大的矩阵,因此我尝试使用 openmp 对其进行并行化。
void ROTATE(MatrixXd &a, int i, int j, int k, int l, double s, double tau)
{
double g,h;
g=a(i,j);
h=a(k,l);
a(i,j)=g-s*(h+g*tau);
a(k,l)=h+s*(g-h*tau);
}
void jacobi(int n, MatrixXd &a, MatrixXd &v, VectorXd &d )
{
int j,iq,ip,i;
double tresh,theta,tau,t,sm,s,h,g,c;
VectorXd b(n);
VectorXd z(n);
v.setIdentity();
z.setZero();
#pragma omp parallel for
for (ip=0;ip<n;ip++)
{
d(ip)=a(ip,ip);
b(ip)=d(ip);
}
for (i=0;i<50;i++)
{
sm=0.0;
for (ip=0;ip<n-1;ip++)
{
#pragma omp parallel for reduction (+:sm)
for (iq=ip+1;iq<n;iq++)
sm += fabs(a(ip,iq));
}
if (sm == 0.0) {
break;
}
if (i < 3)
tresh=0.2*sm/(n*n);
else
tresh=0.0;
#pragma omp parallel for private (ip,g,h,t,theta,c,s,tau)
for (ip=0;ip<n-1;ip++)
{
//#pragma omp parallel for private (g,h,t,theta,c,s,tau)
for (iq=ip+1;iq<n;iq++)
{
g=100.0*fabs(a(ip,iq));
if (i > 3 && (fabs(d(ip))+g) == fabs(d[ip]) && (fabs(d[iq])+g) == fabs(d[iq]))
a(ip,iq)=0.0;
else if (fabs(a(ip,iq)) > tresh)
{
h=d(iq)-d(ip);
if ((fabs(h)+g) == fabs(h))
{
t=(a(ip,iq))/h;
}
else
{
theta=0.5*h/(a(ip,iq));
t=1.0/(fabs(theta)+sqrt(1.0+theta*theta));
if (theta < 0.0)
{
t = -t;
}
c=1.0/sqrt(1+t*t);
s=t*c;
tau=s/(1.0+c);
h=t*a(ip,iq);
#pragma omp critical
{
z(ip)=z(ip)-h;
z(iq)=z(iq)+h;
d(ip)=d(ip)-h;
d(iq)=d(iq)+h;
a(ip,iq)=0.0;
for (j=0;j<ip;j++)
ROTATE(a,j,ip,j,iq,s,tau);
for (j=ip+1;j<iq;j++)
ROTATE(a,ip,j,j,iq,s,tau);
for (j=iq+1;j<n;j++)
ROTATE(a,ip,j,iq,j,s,tau);
for (j=0;j<n;j++)
ROTATE(v,j,ip,j,iq,s,tau);
}
}
}
}
}
}
}
我想并行化执行大部分计算的循环以及代码中插入的两个 cmets:
//#pragma omp parallel for private (ip,g,h,t,theta,c,s,tau)
//#pragma omp parallel for private (g,h,t,theta,c,s,tau)
是我的尝试。不幸的是,它们最终都产生了不正确的结果。我怀疑问题可能出在这个块:
z(ip)=z(ip)-h;
z(iq)=z(iq)+h;
d(ip)=d(ip)-h;
d(iq)=d(iq)+h;
因为通常这种累积需要减少,但由于每个线程访问数组的不同部分,我不确定这一点。
我不确定我是否以正确的方式进行并行化,因为我最近才开始使用 openmp,因此也欢迎任何建议或建议。
旁注:我知道有更快的特征值和特征向量确定算法,包括 Eigen 中的 SelfAdjointEigenSolver,但这些算法并没有给我在特征向量中所需的精度,而这个算法是。
提前致谢。
编辑:我认为正确的答案是 The Quantum Physicist 提供的答案,因为我所做的并没有减少最大为 4096x4096 的系统的计算时间。无论如何,我更正了代码以使其正常工作,也许对于足够大的系统,它可能会有一些用处。我建议使用计时器来测试
#pragma omp for
实际上减少了计算时间。
【问题讨论】:
-
我没有确认并运行此代码...但我猜您的
ROTATE函数在线程之间存在争用。为了证明这一点,您可以尝试输入#pragma omp critical { ... }。此外,除非您需要符合 C89 标准(您使用的是 C++,所以..)...我个人发现将参数范围缩小到最窄范围要容易得多,它使私有/共享在语义上更加明显。 -
关于你的旁注,很高兴弄清楚为什么
SelfAdjointEigenSolver没有给你预期的准确性。为此,您能否向我发布/发送一个独立的示例或您的问题矩阵之一,以便我们可以进行调查? (你会在几乎所有 Eigen 的头文件中找到我的电子邮件)。谢谢。 -
元问题:为什么要自己写?为什么不使用现有的优化数学库中的一个。英特尔 MKL 免费提供 software.intel.com/en-us/articles/free-mkl 并包含 Jacobi 函数。 software.intel.com/en-us/node/522101 我不确定它们是否并行化,但 MKL 的其他部分肯定是并行化的。 (“最好的代码是我不必编写的代码”)。 (FWIW 我为英特尔工作,但不在 MKL)。
-
感谢您的建议,我会检查一下。我想获得特征值和特征向量不能解决线性系统,但也许英特尔库中也有这样做的东西。
标签: c++ algorithm parallel-processing openmp eigen