我有一段代码,看起来像这样,
for(i=0;i<NumberOfSteps;i++)
{
for(k=0;k<NumOfNodes;k++)
{
mark[crawler[k]]++;
r = rand() % node_info[crawler[k]].num_of_nodes;
crawler[k] = (int)DataBlock[node_info[crawler[k]].index+r][0];
}
}
我对其进行了更改,以便可以在多个线程之间分配负载。现在它看起来像这样,
for(i=0;i<NumberOfSteps;i++)
{
for(k=0;k<NumOfNodes;k++)
{
pthread_mutex_lock( &mutex1 );
mark[crawler[k]]++;
pthread_mutex_unlock( &mutex1 );
pthread_mutex_lock( &mutex1 );
r = rand() % node_info[crawler[k]].num_of_nodes;
pthread_mutex_unlock( &mutex1 );
pthread_mutex_lock( &mutex1 );
crawler[k] = (int)DataBlock[node_info[crawler[k]].index+r][0];
pthread_mutex_unlock( &mutex1 );
}
}
我需要互斥锁来保护共享变量。事实证明我的并行代码更慢。但为什么 ?是因为互斥体吗?
这可能与缓存行大小有关吗?
答案 0 :(得分:0)
除了循环头之外,你没有并行化任何东西。锁定和解锁之间的所有内容都被强制执行。而且由于锁定/解锁是(可能)昂贵的操作,代码变得越来越慢。
要解决此问题,您至少应该将昂贵的计算(没有互斥保护)与访问共享数据区域(使用互斥锁)分开。然后尝试将互斥锁移出内循环。
你可以使用原子增量指令(取决于平台)而不是普通的'++',它通常比互斥量便宜。但要注意经常对来自不同线程的单个缓存行的数据进行并行处理(参见'false sharing')。
AFAICS,您可以重写算法,如下所示,根本不需要互斥量和原子增量。如果NumOfNodes是NumOfThreads的整数倍,则getFirstK()为NumOfNodes / NumOfThreads *。
for(t=0;t<NumberOfThreads;t++)
{
kbegin = getFirstK(NumOfNodes, NumOfThreads, t);
kend = getFirstK(NumOfNodes, NumOfThreads, t+1);
// start the following in a separate thread with kbegin and kend
// copied to thread local vars kbegin_ and kend_
int k, i, r;
unsigned state = kend_; // really bad seed
for(k=kbegin_;k<kend_;k++)
{
for(i=0;i<NumberOfSteps;i++)
{
mark[crawler[k]]++;
r = rand_r(&state) % node_info[crawler[k]].num_of_nodes;
crawler[k] = (int)DataBlock[node_info[crawler[k]].index+r][0];
}
}
}
// wait for threads/jobs to complete
这种生成随机数的方法可能导致错误的随机分布,有关详细信息,请参阅this question。