使用pthread会增加执行时间,并提出改进建议

时间:2013-02-04 01:26:00

标签: parallel-processing pthreads mutex latency

我有一段代码,看起来像这样,

 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 );
   }
}

我需要互斥锁来保护共享变量。事实证明我的并行代码更慢。但为什么 ?是因为互斥体吗?

这可能与缓存行大小有关吗?

1 个答案:

答案 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