我目前正在写一个物理模拟(t.b.m.p.求解一个随机微分方程),我需要并行化它。
现在这可以通过MPI来实现,我想我将来需要做一些工作,但是现在我想利用我的本地机器的所有8个内核。对于一个参数集,正常运行需要2-17小时。所以我想利用多线程,特别是下面的函数应该并行执行。对于Nsteps
时间步长,此功能基本上解决了相同的SDE Nrep
次。将结果取平均值并将每个线程存储到Nthreads x Nsteps
阵列JpmArr
的单独行中。多线程和动态数组/矩阵运算的
double **JpmArr;
void worker(const dtype gamma, const dtype dt, const uint seed, const uint Nsteps, const uint Nrep,\
const ESpMatD& Jplus, const ESpMatD& Jminus, const ESpMatD& Jz, const uint tId){
dtype dW(0), stdDev(sqrt(dt));
std::normal_distribution<> WienerDistr(0, stdDev);
//create the arrays for the values of <t|J+J-|t>
dtype* JpmExpect = JpmArr[tId];
//execute Nrep repetitions of the experiment
for (uint r(0); r < Nrep; ++r) {
//reinitialize the wave function
psiVecArr[tId] = globalIstate;
//<t|J+J-|t>
tmpVecArr[tId] = Jminus* psiVecArr[tId];
JpmExpect[0] += tmpVecArr[tId].squaredNorm();
//iterate over the timesteps
for (uint s(1); s < Nsteps; ++s) {
//get a random number
dW = WienerDistr(RNGarr[tId]);
//execute one step of the RK-s.o. 1 scheme
tmpPsiVecArr[tId] = F2(gamma, std::ref(Jminus), std::ref(psiVecArr[tId]));
tmpVecArr[tId] = psiVecArr[tId] + tmpPsiVecArr[tId] * sqrt(dt);
psiVecArr[tId] = psiVecArr[tId] + F1(gamma, std::ref(Jminus), std::ref(Jplus), std::ref(psiVecArr[tId])) * dt + tmpPsiVecArr[tId] * dW \
+ 0.5 * (F2(gamma, std::ref(Jminus), std::ref(tmpVecArr[tId])) - F2(gamma, std::ref(Jminus), std::ref(psiVecArr[tId]))) *(dW * dW - dt)/sqrt(dt);
//normalise
psiVecArr[tId].normalize();
//compute <t|J+J-|t>
tmpVecArr[tId] = Jminus* psiVecArr[tId];
JpmExpect[s] += tmpVecArr[tId].squaredNorm();
}
}
//average over the repetitions
for (uint j(0); j < Nsteps; ++j) {
JpmExpect[j] /= Nrep;
}
}
我使用Eigen作为线性代数库,因而:
typedef Eigen::SparseMatrix<dtype, Eigen::RowMajor> ESpMatD;
typedef Eigen::Matrix<dtype, Eigen::Dynamic, Eigen::RowMajor> VectorXdrm;
被用作类型。上述工函数调用:
VectorXdrm& F1(const dtype a, const ESpMatD& A, const ESpMatD& B, const VectorXdrm& v) {
z.setZero(v.size());
y.setZero(v.size());
// z is for simplification
z = A*v;
//scalar intermediate value c = <v, Av>
dtype c = v.dot(z);
y = a * (2.0 * c * z - B * z - c * c * v);
return y;
}
VectorXdrm& F2(const dtype a, const ESpMatD& A, const VectorXdrm& v) {
//zero the data
z.setZero(v.size());
y.setZero(v.size());
z = A*v;
dtype c = v.dot(z);
y = sqrt(2.0 * a)*(z - c * v);
return y;
}
其中矢量z,y
VectorXdrm
是类型,并且在相同的文件(模块全局)被声明。 所有的数组(RNGarr, JpmArr, tmpPsiVecArr, tmpVecArr, psiVecArr
)都在main中初始化(使用main.cpp
中的extern
声明)。在完成设置后,我使用std::async
运行函数,等待所有完成,然后将JpmArr
中的数据收集到main()
中的单个数组中,并将其写入文件。
问题: 如果我使用std::launch::async
,结果是无稽之谈。 如果我使用std::launch::deferred
,计算结果和平均结果匹配(直到数值方法允许)我通过分析手段获得的结果。
我不知道东西失败的地方。我曾经使用Armadillo作为线性代数,但它的normalize
例程交付了nan
's,所以我切换到Eigen,它提示(在文档中)可用于多个线程 - 它仍然失败。 在我花了4天的时间之前没有和线程一起工作,现在试图让这个工作和阅读的东西。后者导致我使用全局数组RNGarr, JpmArr, tmpPsiVecArr, tmpVecArr, psiVecArr
(之前我只是试图在worker
中创建合适的数组,并将结果通过struct workerResult
返回到main。)以及使用std::ref()
将矩阵Jplus, Jminus, Jz
传递给该函数。 (为了简洁起见,上面的功能被省略了) -
但是我得到的结果仍然是错误的,我不再有任何想法了,什么是错的,我应该怎么做才能获得正确的结果。任何输入和/或指向此类(线程)问题或参考解决方案示例的指针都将不胜感激。
你能展示实际调用启动:: async的代码?能够运行多个线程的关键要求是所有的代码都应该是线程安全的(这很难确定,但是在C++中改变为true比在C中更好),并且每个线程应该在其自己的数据结构上工作(或一些程序上的保证,他们没有彼此踩脚趾) – Soren
'auto ws = std :: async(std :: launch :: deferred,&worker,gamma,dt,RNGseed [s],Nsteps,NrepPt,std :: ref(Jplus),std :: ref(Jminus),std :: ref(Jz),tId ++);'哪里s'是0,1,2,3现在手动设置 – Nox
Jplus,Jminus - 它们是引用其他矩阵?在工作人员正在运行时他们是否会被另一个线程更新? – Soren