1

我已经为矩阵向量乘法编写了代码。矩阵根据线程数被划分为行块,每个块乘以向量,向量存储在线程私有的数组中。但是我的加速很差。对于大小为 16 X 16 的矩阵,它小于 1。

这可能是因为我将我的矩阵和向量在外部声明为共享变量,并且当每个线程尝试从矩阵和向量中读取值时可能会导致竞争条件/错误共享?

我对虚假共享和竞争条件有点困惑。

#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
#define  SIZE 128               // The size should be divisible by thenumber of threads

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

int thread_count = strtol(argv[1],NULL,10);
// Declare the variables
int i,j;
long A[SIZE][SIZE], b[SIZE],V[SIZE]={0};
//long Vect[SIZE]={0};
double start, end;
// Generate a matrix of size mxm
for (i=0; i<SIZE; i++)
{   for (j=0; j<SIZE; j++)
    A[i][j] = i+j;
}

printf("The Matrix is:\n");
// Print the Matrix
for (i=0; i<SIZE; i++)
{   for (j=0; j<SIZE; j++)
        {
        printf("%12ld", A[i][j]);
        }
printf("\n");

}

// Generate a vector of size m
for (i=0; i<SIZE; i++)
    b[i] = i;

printf("The vector is: \n");
// Print a vector
for (i=0; i<SIZE; i++)
    printf("%12ld\n", b[i]);


start = omp_get_wtime();
//omp_set_num_threads(NUM_THREADS);

#pragma omp parallel num_threads(thread_count)
{
int i,j,k, id, nthrds;
long Vect[SIZE]={0};
id = omp_get_thread_num();
nthrds = omp_get_num_threads(); 
for (i=id*SIZE/nthrds; i<(id*SIZE/nthrds + SIZE/nthrds); i++)
{   Vect[i] = 0;
    {
        for (j=0; j<SIZE; j++)
        Vect[i] += A[i][j]*b[j];
    }

}

#pragma omp critical
{
for (k=0; k<SIZE; k++)
V[k] += Vect[k]; 
}
}


end = omp_get_wtime();
printf("The vector obtained after multiplication is:\n");
for (i=0; i<SIZE; i++)
printf("%12ld\n", V[i]);
printf("The time taken for calculation is: %lf\n", end - start);


return 0;

}
4

1 回答 1

0

让我提出一些建议来改进您的代码。

  1. 手动并行化 for 循环很少是一个好主意或必要的。一个原因是它容易出错。

    for (i=id*SIZE/nthrds; i<(id*SIZE/nthrds + SIZE/nthrds); i++)
    

    应该改为

    for (i=id*SIZE/nthrds; i<((id+1)*SIZE/nthrds; i++)
    

    否则对于某些值nthrds的结果是错误的。

    但是,与其自己定义块,不如让 OpenMP 为您做这件事。

    #pragma omp parallel for private(j)
    for(i=0; i<SIZE; i++) {
        long sum = 0;
        for(j=0; j<SIZE; j++) {
            sum += A[i][j]*b[j];
        }
        V[i] += sum;
    }
    
  2. 您在写信时担心虚假分享是对的V。但是,不需要Vect为每个线程定义一个数组。sum上面的代码通过在内部循环中定义来解决您担心的错误共享。该代码仍然具有错误共享,但不是针对所有迭代( ) i,而是仅针对所有迭代()。jSIZE*SIZEiSIZE

  3. 128 的ASIZE太小,无法克服 OpenMP 开销。当我使用 8192 的大小时,我看到对串行代码的显着改进。但是您的代码对于大尺寸还有另一个问题,因为您使用了受堆栈大小限制的数组的自动变量。我建议您使用不受堆栈大小限制的静态变量。

  4. 最后,使用 . 比较串行代码是不公平的num_threads。原因是编译器甚至为num_threads(1). 这会使结果产生偏差。相反,您应该比较启用和不启用 OpenMP。不幸的是,GCC 不允许您在omp_get_wtime()不启用 OpenMP 的情况下使用(尽管 MSVC 和 ICC 可以)。因此,如果您使用 GCC 在比较串行代码时注释掉编译指示。使用 ICC,您只能启用存根功能。使用 MSVC 不要启用 OpenMP(omp_get_wtime()仍然有效)。

以下是解决上述每一点的代码:

#include <stdio.h>
#include <omp.h>
#define  SIZE 8192

int main(void) {
    int i,j;
    double dtime;
    static long A[SIZE][SIZE], b[SIZE],V[SIZE];
    for (i=0; i<SIZE; i++) {
        for (j=0; j<SIZE; j++) {
            A[i][j] = i+j;
        }
    }
    for (i=0; i<SIZE; i++) b[i] = i;

    dtime = -omp_get_wtime();
    #pragma omp parallel for private(j) //comment out for one thread
    for(i=0; i<SIZE; i++) {
        long sum = 0;
        for(j=0; j<SIZE; j++) {
            sum += A[i][j]*b[j];
        }
        V[i] += sum;
    }    
    dtime += omp_get_wtime();
    printf("The time taken for calculation is: %lf\n", dtime);

    return 0;
}
于 2015-02-25T09:43:49.447 回答