我正在编写一个 C 程序,用于pthreads
执行一些矩阵乘法 (C=A*B),然后计算结果的最大行和范数,这是唯一需要同步的部分,因为矩阵乘法本身是独立分布的。每个线程获取自己的矩阵 A 行的切片,然后将其行乘以 B 并将它们存储到 C 的相应行中。因此,所有线程在读取相同的内存位置时写入不同的内存位置,而不会被修改。
现在的问题是,当我在我的 MacBook(2012 年中,双核)上用两个线程编译程序时,无论我制作多大的矩阵,线程版本几乎与没有线程的版本一样快。可以预期,对于小矩阵大小,线程生成的开销等会消除加速,但这不是这里的问题。奇怪的是,如果我在 Red Hat Linux 服务器上运行相同的程序,加速非常明显,而在我的计算机上,串行版本总是快一点。服务器有 16 个内核,但我只使用两个线程(否则将毫无意义)。
有没有人有任何建议为什么同一个程序在服务器上显示加速但在我的 MacBook 上没有?这是一些代码,因此您可以看到我在做什么。
编辑:我尝试在 Mac 上使用 clang 而不是 gcc 进行编译,并且 - 神奇地 - 正如你所期望的那样,加速就在那里。欢迎任何解释,为什么 gcc 不能在不同的 CPU 上分配线程,但 clang 可以。有人会认为我不是第一个经历这种情况的人。
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
#include "assignment3.h"
int main(int argc, const char *argv[])
{
int n, p = 1;
printf("Please enter the number of processors.\n > ");
scanf("%d", &p);
do {
printf("Please enter the matrix factor "
"(matrix size is a multiple of processor number).\n > ");
scanf("%d", &n);
} while(n % p);
/* printf("n and p have values %d and %d\n", n, p); */
// make matrices
double **A, **B, **C;
A = malloc(n*sizeof(double *));
B = malloc(n*sizeof(double *));
C = malloc(n*sizeof(double *));
if (A == NULL || B == NULL || C == NULL) {
return 1;
}
A[0] = malloc(n*n*sizeof(double));
B[0] = malloc(n*n*sizeof(double));
C[0] = calloc(n*n, sizeof(double));
int i;
for (i = 1; i < n; i++) {
A[i] = A[0] + i*n;
B[i] = B[0] + i*n;
C[i] = C[0] + i*n;
}
// Fill with random values between 0 and 1
fillMatrix(n, A);
fillMatrix(n, B);
pthread_t *workers = malloc(p * sizeof(pthread_t));
thread_data_t *data = malloc(p * sizeof(thread_data_t));
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
double global_norm;
void **status = malloc(sizeof(void *)); // leaving out malloc is fine on osx, not on linux
for (i = 0; i < p; i++) {
data[i].n = n;
data[i].p = p;
data[i].my_row = i * n/p;
data[i].A = A;
data[i].B = B;
data[i].C = C;
data[i].mutex = &mutex;
data[i].global_norm = &global_norm;
}
struct timeval tv1, tv2;
struct timezone tz;
gettimeofday(&tv1, &tz);
for (i = 0; i < p; i++) // multiply uses ATLAS to multiply this thread's portion of the matrices
pthread_create(&workers[i], NULL, multiply, &data[i]);
for (i = 0; i < p; i++) pthread_join(workers[i], status);
gettimeofday(&tv2, &tz);
double elapsed = (double) (tv2.tv_sec - tv1.tv_sec) + (double)
(tv2.tv_usec - tv1.tv_usec) * 1.e-6;
// do non-parallel computation
struct timeval tv3, tv4;
struct timezone tz2;
gettimeofday(&tv3, &tz2);
double global_norm_serial = multiply_serial(A, B, C, n); // plain serial multiply with ATLAS
gettimeofday(&tv4, &tz2);
double elapsed_serial = (double) (tv4.tv_sec - tv3.tv_sec) + (double)
(tv4.tv_usec - tv3.tv_usec) * 1.e-6;
printf("Time elapsed for parallel execution: %lf seconds.\n", elapsed);
printf("Time elapsed for serial execution: %lf seconds.\n", elapsed_serial);
/* puts("Matrix A:"); */
/* printMatrix(n, A); */
/* puts("Matrix B:"); */
/* printMatrix(n, B); */
/* puts("Matrix C:"); */
/* printMatrix(n, C); */
printf("The maximum row sum norm is %lf.\n", global_norm);
printf("The maximum (serial) row sum norm is %lf.\n", global_norm_serial);
free(A[0]);
free(B[0]);
free(C[0]);
free(A);
free(B);
free(C);
free(workers);
free(data);
pthread_mutex_destroy(&mutex);
return 0;
}
EDIT2:
我不知道这有什么帮助,但这就是multiply
程序的作用:
void *multiply(void *arg){
thread_data_t *data = arg;
int n = data->n,
p = data->p,
my_row = (*data).my_row;
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n/p, n, n, 1.0,
(data->A)[my_row], n, (data->B)[0], n, 0.0, (data->C)[my_row], n);
pthread_mutex_lock(data->mutex);
double max = findMax(data->C + my_row, n, n/p, data->my_row);
if (*(data->global_norm) < max)
*(data->global_norm) = max;
pthread_mutex_unlock(data->mutex);
pthread_exit(NULL);
return NULL;
}
该类型thread_data_t
具有以下成员:
typedef struct {
int n, p, my_row;
double **A;
double **B;
double **C;
double *global_norm;
pthread_mutex_t *mutex;
} thread_data_t;