【问题标题】:OpenMP parallel multiplication slower than Sequential multiplicationOpenMP 并行乘法比顺序乘法慢
【发布时间】:2020-03-25 02:58:15
【问题描述】:

我正在学习 OpenMP,我正在尝试做一个简单的任务:A[r][c] * X[c] = B[r] (矩阵向量乘法)。 问题是:顺序代码比并行代码快,我不知道为什么! 我的代码:

#include <omp.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/types.h>


// Defined variables
#define row_matriz_A 80000
#define col_matriz_A 800
#define THREADS_NUM 4

// FUNCAO - GERAR MATRIZES
void gerarMatrizes(int r, int c, int mA[], int vX[], int vB[]){...}

// FUNCAO - SEQUENTIAL MULTIPLICATION
void multSequencial(int r, int c, int mA[], int vX[], int vB[]){
    // Variables
    int i, j, offset, sum;                        
    struct timeval tv1,tv2;  
    double t1, t2;        
    // Begin Time
    gettimeofday(&tv1, NULL);
    t1 = (double)(tv1.tv_sec) + (double)(tv1.tv_usec)/ 1000000.00;
    for(i = 0; i < r; i++){
        sum = 0;
        for(j = 0; j < c; j++){
            offset = i * c + j;
            sum += mA[offset] * vX[j];
        }
        vB[i] = sum;
    }
    // End time
    gettimeofday(&tv2, NULL);
    t2 = (double)(tv2.tv_sec) + (double)(tv2.tv_usec)/ 1000000.00;
    printf("\nO tempo de execucao sequencial foi: %lf segundos.\n", (t2 - t1));
    return;
}

// FUNCAO - MULTIPLICACAO PARALELA COM OpenMP
void matvecHost(int r, int c, int mA[], int vX[], int vB[]){
    // Variaveis
    int tID, i, j, offset, sum;
    struct timeval tv1, tv2;
    double t1, t2;
    // Init vB
    for(i = 0; i < r; i++) vB[i] = 0;
    // BEGIN Time
    gettimeofday(&tv1, NULL);
    t1 = (double)(tv1.tv_sec) + (double)(tv1.tv_usec)/ 1000000.00;
    omp_set_num_threads(THREADS_NUM);
    #pragma omp parallel private(tID, i, j) shared(mA, vB, vX)
    {
        tID = omp_get_thread_num();     
        #pragma omp for
            for(i = 0; i < r; i++){
                sum = 0;
                for(j = 0; j < c; j++){
                    offset = i * c + j;
                    sum += mA[offset] * vX[j];
                }
                vB[i] = sum;
            }
    }
    // End time
    gettimeofday(&tv2, NULL);
    t2 = (double)(tv2.tv_sec) + (double)(tv2.tv_usec)/ 1000000.00;
    printf("\nO tempo de execucao OpenMP foi: %lf segundos.\n", (t2 - t1));
    return;
}

// FUNCAO - PRINCIPAL
int main(int argc, char * argv[]) {
    int row, col;
    row = row_matriz_A;
    col = col_matriz_A;
    int *matrizA = (int *)calloc(row * col, sizeof(int));
    int *vectorX = (int *)calloc(col * 1, sizeof(int));
    int *vectorB = (int *)calloc(row * 1, sizeof(int));
    gerarMatrizes(row, col, matrizA, vectorX, vectorB);                    
    multSequencial(row, col, matrizA, vectorX, vectorB);
    matvecHost(row, col, matrizA, vectorX, vectorB);
    return 0;
}

以前没有有效的解决方案:

  • 在我的平方中使用折叠
  • 增加行数和列数
  • 增加线程数(老师推荐使用线程数==线程物理数)
  • 使用 malloc 代替 m[i][j]

编辑 - 回答

根据正确答案正确更改了我的并行块:

#pragma omp parallel private(i, j, sum) shared(mA, vB, vX)
{
    #pragma omp for
        for(i = 0; i < r; i++){
            sum = 0;
            for(j = 0; j < c; j++){
                sum += mA[i * c + j] * vX[j];
            }
            vB[i] = sum;
        }
}

我还是有点疑惑:

  • 如果我定义ijsum 我的并行块,它们会自动设置为私有?这是否提高了我的代码速度?

【问题讨论】:

    标签: c vector openmp matrix-multiplication


    【解决方案1】:

    您在 sumoffset 上有竞争条件 - 它们在线程之间共享,而不是线程私有。

    这也可能解释了减速的原因:在 x86 上,CPU 实际上会努力工作以确保对共享变量的访问“有效”。这涉及在每次(!)写入 offsetsum 之后刷新缓存行 - 所以所有线程都在疯狂地写入相同的变量,但每个线程都必须等到前一个线程的写入(在不同的核心上) 在刷新后再次到达本地缓存。当然,它会产生完全荒谬的结果。

    我不知道您为什么要在函数开头声明所有变量 - 这很容易出现这类错误。如果您在尽可能小的范围内声明ijsumoffset(以及未使用的tID),您就不会遇到这个问题,因为它们会自动在那个案例。

    【讨论】:

    • 同样值得注意的是,即使没有像 OP 的代码这样的问题,也不能假设并行化会自动提供性能优势。同时执行的收益(如果有的话)被建立和管理并行化的开销所抵消。
    • @JohnBollinger 当然,它不会总是提供性能改进,但在这里它应该。该代码会将 80k 行迭代分成四块 20k 全 vec-vec 乘法。因此,每个块会有数百万个 madds - 绝对足以忽略纯线程开销(尽管内存带宽/缓存是另一回事)。
    • @MaxLanghof 感谢您的帮助,现在我意识到我的代码中的错误!我将进行编辑以显示我的代码现在如何,但我有一些疑问...
    猜你喜欢
    • 1970-01-01
    • 2014-05-03
    • 2013-05-18
    • 1970-01-01
    • 2017-09-14
    • 1970-01-01
    • 2018-08-24
    • 2015-10-16
    • 1970-01-01
    相关资源
    最近更新 更多