我正在尝试使用boost库多线程化一段代码。问题是每个线程都必须访问和修改几个全局变量。我正在使用互斥锁来锁定共享资源,但是当程序没有多线程时,程序最终需要花费更多时间。关于如何优化共享访问的任何建议?
非常感谢!
在下面的例子中,* choose_ecount *变量必须被锁定,我不能把它从循环中取出并锁定它只在循环结束时进行更新,因为它需要最新的值由内部功能。
for(int sidx = startStep; sidx <= endStep && sidx < d.sents[lang].size(); sidx ++){
sentence s = d.sents[lang][sidx];
int senlen = s.words.size();
int end_symb = s.words[senlen-1].pos;
inside(s, lbeta);
outside(s,lbeta, lalpha);
long double sen_prob = lbeta[senlen-1][F][NO][0][senlen-1];
if (lambda[0] == 0){
mtx_.lock();
d.sents[lang][sidx].prob = sen_prob;
mtx_.unlock();
}
for(int size = 1; size <= senlen; size++)
for(int i = 0; i <= senlen - size ; i++)
{
int j = i + size - 1;
for(int k = i; k < j; k++)
{
int hidx = i; int head = s.words[hidx].pos;
for(int r = k+1; r <=j; r++)
{
int aidx = r; int arg = s.words[aidx].pos;
mtx_.lock();
for(int kids = ONE; kids <= MAX; kids++)
{
long double num = lalpha[hidx][R][kids][i][j] * get_choose_prob(s, hidx, aidx) *
lbeta[hidx][R][kids - 1][i][k] * lbeta[aidx][F][NO][k+1][j];
long double gen_right_prob = (num / sen_prob);
choose_ecount[lang][head][arg] += gen_right_prob; //LOCK
order_ecount[lang][head][arg][RIGHT] += gen_right_prob; //LOCK
}
mtx_.unlock();
}
}
答案 0 :(得分:1)
从您发布的代码中我只能看到对choose_ecount和order_ecount的写入。那么为什么不使用本地每线程缓冲区来计算总和,然后在最外层循环后添加它们并仅同步此操作?
编辑: 如果您需要访问choose_ecount的中间值,您如何确保存在正确的中间值?一个线程可能已完成其循环的2次迭代,同时在另一个线程中产生不同的结果。
听起来你需要使用屏障进行计算。
答案 1 :(得分:0)
在内循环中使用互斥锁不太可能获得可接受的性能。并发编程很难,不仅适用于程序员,也适用于计算机。现代CPU的很大一部分性能来自于能够将代码块视为独立于外部数据的序列。对单线程执行有效的算法通常不适合多线程执行。
你可能想看看boost::atomic
,它可以提供无锁同步,但原子操作所需的内存障碍仍然不是免费的,所以你可能仍会遇到问题,你可能会必须重新考虑你的算法。
答案 2 :(得分:0)
我猜您将整个问题划分为从startStep
到endStep
的块,以便由每个线程进行处理。
由于你已经锁定mutex
,所以你有效地序列化所有线程:
您将问题划分为一些块,这些块以串行但未指定的顺序处理。
这是你唯一得到的就是多线程的开销。
由于您在double
上运行,因此使用原子操作不是您的选择:它们通常仅针对整数类型实现。
唯一可行的解决方案是遵循Kratz的建议,为每个线程提供choose_ecount
和order_ecount
的副本,并在线程完成后将它们减少为一个。