【问题标题】:Matrix Multiplication with Threads: Why is it not faster?带线程的矩阵乘法:为什么不更快?
【发布时间】:2010-06-06 22:29:35
【问题描述】:

所以我一直在玩 pthreads,特别是试图计算两个矩阵的乘积。我的代码非常混乱,因为它只是对我自己来说是一个快速有趣的项目,但我使用的线程理论非常相似:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define M 3
#define K 2
#define N 3
#define NUM_THREADS 10

int A [M][K] = { {1,4}, {2,5}, {3,6} };
int B [K][N] = { {8,7,6}, {5,4,3} };
int C [M][N];

struct v {
   int i; /* row */
   int j; /* column */
};

void *runner(void *param); /* the thread */

int main(int argc, char *argv[]) {

   int i,j, count = 0;
   for(i = 0; i < M; i++) {
      for(j = 0; j < N; j++) {
         //Assign a row and column for each thread
         struct v *data = (struct v *) malloc(sizeof(struct v));
         data->i = i;
         data->j = j;
         /* Now create the thread passing it data as a parameter */
         pthread_t tid;       //Thread ID
         pthread_attr_t attr; //Set of thread attributes
         //Get the default attributes
         pthread_attr_init(&attr);
         //Create the thread
         pthread_create(&tid,&attr,runner,data);
         //Make sure the parent waits for all thread to complete
         pthread_join(tid, NULL);
         count++;
      }
   }

   //Print out the resulting matrix
   for(i = 0; i < M; i++) {
      for(j = 0; j < N; j++) {
         printf("%d ", C[i][j]);
     }
      printf("\n");
   }
}

//The thread will begin control in this function
void *runner(void *param) {
   struct v *data = param; // the structure that holds our data
   int n, sum = 0; //the counter and sum

   //Row multiplied by column
   for(n = 0; n< K; n++){
      sum += A[data->i][n] * B[n][data->j];
   }
   //assign the sum to its coordinate
   C[data->i][data->j] = sum;

   //Exit the thread
   pthread_exit(0);
}

来源:http://macboypro.com/blog/2009/06/29/matrix-multiplication-in-c-using-pthreads-on-linux/

对于非线程版本,我使用了相同的设置(3 个二维矩阵,动态分配的结构来保存 r/c),并添加了一个计时器。第一次试验表明非线程版本更快。我的第一个想法是尺寸太小而无法注意到差异,并且创建线程需要更长的时间。所以我将尺寸提高到大约 50x50,随机填充并运行它,但我仍然没有看到线程版本的任何性能提升。

我在这里错过了什么?

【问题讨论】:

  • 您在哪种处理器上运行它?如果它不是多线程或双核,您将看不到使用多线程的任何优势。事实上,为了同时运行两个线程而必须进行的上下文切换实际上会损害性能。
  • 50x50 矩阵对于现代计算机来说仍然太小。如果您正在寻找性能问题,您将不得不转向更大的尺寸——数万行和数万列。那么寻找优化(例如多线程)是有意义的。

标签: c multithreading


【解决方案1】:

除非您使用非常 大矩阵(数千行/列),否则您不太可能从这种方法中看到很大的改进。相对于 CPU 时间而言,在现代 CPU/OS 上设置线程实际上非常昂贵,比几次乘法运算要多得多。

此外,通常不值得为每个可用的 CPU 内核设置多个线程。例如,如果您只有两个内核并且设置了 2500 个线程(对于 50x50 矩阵),那么操作系统将花费所有时间来管理和在这 2500 个线程之间切换,而不是进行计算。

如果您要预先设置两个线程(仍然假设一个双核 CPU),请让这些线程一直可用以等待工作完成,并为它们提供您需要以某种方式计算的 2500 点积同步工作队列,那么您可能开始看到改进。但是,它仍然不会比只使用一个内核好 50% 以上。

【讨论】:

  • 需要注意的是,您有一个 UI 线程和一个工作线程。
  • @Chris Thompson:您的 UI 线程不太可能占用大量 CPU 资源。拥有单独的 UI 线程的好处是在进行计算时不会阻塞您的 UI 线程,从而使您的 UI 保持响应。
【解决方案2】:

我不完全确定我理解源代码,但它看起来是这样的:你有一个运行 M*N 次的循环。每次循环时,您都会创建一个线程,在结果矩阵中填充一个数字。但是在你启动线程之后,你等待它完成。我认为您实际上从未运行过多个线程。

即使您运行了多个线程,该线程也只做微不足道的工作。即使 K 很大(您提到 50),与首先启动线程的成本相比,50 次乘法也不算多。程序应该创建更少的线程——当然不超过处理器的数量——并为每个线程分配更多的工作。

【讨论】:

    【解决方案3】:

    您不允许太多并行执行:您在创建线程后立即等待线程,因此您的程序几乎无法使用额外的 CPU(即它永远不能使用第三个 CPU/内核)。尝试允许更多线程运行(可能达到您拥有的核心数)。

    【讨论】:

      【解决方案4】:

      如果您有一个具有两个内核的处理器,那么您应该将要完成的工作分成两半,并给每个线程各一半。如果您有 3、4、5 个内核,则相同的原理。最佳性能设计将始终使线程数与可用内核数相匹配(可用我指的是尚未被其他进程大量使用的内核)。

      您必须考虑的另一件事是每个线程的数据必须是连续的并且独立于其他线程的数据。否则,memcache 未命中将显着减慢处理速度。

      为了更好地理解这些问题,我推荐并行编程模式这本书 http://astore.amazon.com/amazon-books-20/detail/0321228111

      虽然它的代码示例更多地针对 OpenMP 和 MPI,而且您使用的是 PThreads,但本书的前半部分仍然非常丰富的基本概念和多线程环境的内部工作,对于避免大部分性能非常有用你会遇到的瓶颈。

      【讨论】:

        【解决方案5】:

        如果代码并行化正确(我不会检查它),只有当代码在硬件中并行化,即线程是真正并行的(多核、多 cpu ......其他技术......)时,性能可能才会提升并且显然不是(“多任务”方式)并行。只是一个想法,我不确定是不是这样。

        【讨论】:

          猜你喜欢
          • 2015-01-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-06
          • 1970-01-01
          • 1970-01-01
          • 2011-05-31
          • 1970-01-01
          相关资源
          最近更新 更多