【问题标题】:C multithread performance issueC多线程性能问题
【发布时间】:2015-04-19 22:31:47
【问题描述】:

我正在编写一个多线程程序来遍历一个n x n矩阵,其中主对角线中的元素是并行处理的,如下代码所示:

int main(int argc, char * argv[] )
{   
  /* VARIABLES INITIALIZATION HERE */

  gettimeofday(&start_t, NULL); //start timing
  for (int slice = 0; slice < 2 * n - 1; ++slice)
  {  
    z = slice < n ? 0 : slice - n + 1;
    int L = 0;
    pthread_t threads[slice-z-z+1];
    struct thread_data td[slice-z-z+1];

    for (int j=z; j<=slice-z; ++j)
    {
      td[L].index= L;
      printf("create:%d\n", L );
      pthread_create(&threads[L],NULL,mult_thread,(void *)&td[L]);
      L++;
    }

    for (int j=0; j<L; j++) 
    {
      pthread_join(threads[j],NULL);
    }
  }     

  gettimeofday(&end_t, NULL); 
  printf("Total time taken by CPU: %ld \n", ( (end_t.tv_sec - start_t.tv_sec)*1000000 + end_t.tv_usec - start_t.tv_usec));

  return (0);
}

void *mult_thread(void *t)
{      
  struct thread_data *my_data= (struct thread_data*) t;

  /* SOME ADDITIONAL CODE LINES HERE */ 

  printf("ThreadFunction:%d\n", (*my_data).index );

  return (NULL);
}

问题在于,与串行(幼稚)实现相比,这种多线程实现给了我非常糟糕的性能。

是否可以进行一些调整来提高多线程版本的性能?

【问题讨论】:

  • 您是否尝试过识别可能受益于多线程执行的代码部分?向程序添加更多线程并不会自动使其更快。
  • 每个线程完成的任务是否足够大以克服开销?此外,使用最大数量的线程可能是一个想法,因为您的系统只能处理 4、8、16 等线程
  • 我可以想象单线程版本从缓存中受益,而多线程版本则不能,因为内存访问是随机进入的。看看this question。也许尝试将矩阵存储为对角线?
  • 创建一个新线程也有一定的成本。如果“这里的一些附加代码行”中的成本不比线程创建的成本高多少,它甚至可能会使性能变差。你可以先创建一些线程(2 * cpu 线程,以我的经验),然后写给你任务到队列或其他结构。
  • 您错过了@RobertHarvey 和其他人提出的观点。使用多线程不会自动提高性能。在某些情况下,它甚至会导致性能下降。向我们展示“附加代码行”。没有它,没有人可以告诉您是否做错了什么不会导致性能提升,或者您尝试解决的问题是否没有从多线程中受益。

标签: c linux multithreading matrix


【解决方案1】:

线程池可能会更好。

定义一个新的结构类型如下。

typedef struct {
    struct thread_data * data;
    int status; // 0: ready 
                // 1: adding data 
                // 2: data handling, 3: done
    int next_free;
} thread_node;

初始化:

size_t thread_size = 8;
thread_node * nodes = (thread_node *)malloc(thread_size * sizeof(thread_node));
for(int i = 0 ; i < thread_size - 1 ; i++ ) {
    nodes[i].next_free = i + 1;
    nodes[i].status = 0 ; 
}
nodes[thread_size - 1].next_free = -1;
int current_free_node = 0 ;
pthread_mutex_t mutex;

获取线程:

int alloc() {
    pthread_mutex_lock(&mutex);
    int rt = current_free_node;
    if(current_free_node != -1) {
        current_free_node = nodes[current_free_node].next_free;
        nodes[rt].status = 1;
    }
    pthread_mutex_unlock(&mutex);
    return rt;
}

返回线程:

void back(int idx) {
    pthread_mutex_lock(&mutex);
    nodes[idx].next_free = current_free_node;
    current_free_node = idx;
    nodes[idx].status = 0;
    pthread_mutex_unlock(&mutex);
}

先创建线程,然后使用alloc()尝试获取空闲线程,更新指针。

  • 不要用join来判断状态。
  • 将您的 mult_thread 修改为循环,在作业完成后,只需将您的状态更改为 3
  • 对于线程中的每个循环,你可以给它更多的工作

希望能给你一些帮助。

------------ 2015 年 4 月 23 日更新 --------------------

here 就是一个例子。

使用命令编译和运行 $ g++ thread_pool.cc -o tp -pthread --std=c++

yu:thread_pool yu$ g++ tp.cc -o tp  -pthread --std=c++11 && ./tp
1227135.147 1227176.546 1227217.944 1227259.340...
time cost 1 : 1068.339091 ms
1227135.147 1227176.546 1227217.944 1227259.340...
time cost 2 : 548.221607 ms

您也可以删除计时器,它也可以编译为标准 c99 文件。

目前,线程大小限制为2。您也可以调整参数thread_size,重新编译运行。更多线程可能会给您带来更多优势(在我的电脑中,如果我将线程大小更改为 4,任务将在 280 毫秒内完成),而如果您没有足够的 cpu 线程,过多的线程数可能对您没有太大帮助。

【讨论】:

  • 由于我是 pthread 编程的新手,因此在理解您建议的更新的某些部分时遇到了一些困难: - 您是否建议使用固定数量的线程,即 8? - 如何调用函数分配和返回?如果您能解释上述问题,我将不胜感激
  • @MROF 我可以知道最终结果吗?如果它可以回答您的问题,请您点击“接受”按钮吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多