【问题标题】:how to optimize and speed up the multiplication of matrix in c++?如何优化和加速 C++ 中的矩阵乘法?
【发布时间】:2019-08-10 05:11:48
【问题描述】:

这是矩阵乘法的优化实现,该例程执行矩阵乘法运算。 C := C + A * B(其中 A、B 和 C 是以列优先格式存储的 n×n 矩阵) 退出时,A 和 B 保持其输入值。

void matmul_optimized(int n, int *A, int *B, int *C)
{
    // to the effective bitwise calculation
    // save the matrix as the different type
    int i, j, k;
    int cij;
    for (i = 0; i < n; ++i) {
        for (j = 0; j < n; ++j) {
            cij = C[i + j * n]; // the initialization into C also, add separate additions to the product and sum operations and then record as a separate variable so there is no multiplication
            for (k = 0; k < n; ++k) {
                cij ^= A[i + k * n] & B[k + j * n]; // the multiplication of each terms is expressed by using & operator the addition is done by ^ operator.
            }
            C[i + j * n] = cij; // allocate the final result into C         }
    }
}

如何根据上述函数/方法加快矩阵乘法的速度?

这个函数被测试到 2048 x 2048 矩阵。

函数 matmul_optimized 是用 matmul 完成的。

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

#include "cpucycles.c"
#include "helper_functions.c"
#include "matmul_reference.c"
#include "matmul_optimized.c"

int main()
{
    int i, j;
    int n = 1024; // Number of rows or columns in the square matrices
    int *A, *B;   // Input matrices
    int *C1, *C2; // Output matrices from the reference and optimized implementations

    // Performance and correctness measurement declarations
    long int CLOCK_start, CLOCK_end, CLOCK_total, CLOCK_ref, CLOCK_opt;
    long int COUNTER, REPEAT = 5;
    int difference;
    float speedup;

    // Allocate memory for the matrices
    A = malloc(n * n * sizeof(int));
    B = malloc(n * n * sizeof(int));
    C1 = malloc(n * n * sizeof(int));
    C2 = malloc(n * n * sizeof(int));

    // Fill bits in A, B, C1
    fill(A, n * n);
    fill(B, n * n);
    fill(C1, n * n);

    // Initialize C2 = C1
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            C2[i * n + j] = C1[i * n + j];

    // Measure performance of the reference implementation
    CLOCK_total = 0;
    for (COUNTER = 0; COUNTER < REPEAT; COUNTER++)
    {
        CLOCK_start = cpucycles();
        matmul_reference(n, A, B, C1);
        CLOCK_end = cpucycles();
        CLOCK_total = CLOCK_total + CLOCK_end - CLOCK_start;
    }
    CLOCK_ref = CLOCK_total / REPEAT;
    printf("n=%d Avg cycle count for reference implementation = %ld\n", n, CLOCK_ref);

    // Measure performance of the optimized implementation
    CLOCK_total = 0;
    for (COUNTER = 0; COUNTER < REPEAT; COUNTER++)
    {
        CLOCK_start = cpucycles();
        matmul_optimized(n, A, B, C2);
        CLOCK_end = cpucycles();
        CLOCK_total = CLOCK_total + CLOCK_end - CLOCK_start;
    }
    CLOCK_opt = CLOCK_total / REPEAT;
    printf("n=%d Avg cycle count for optimized implementation = %ld\n", n, CLOCK_opt);

    speedup = (float)CLOCK_ref / (float)CLOCK_opt;

    // Check correctness by comparing C1 and C2
    difference = 0;
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            difference = difference + C1[i * n + j] - C2[i * n + j];

    if (difference == 0)
        printf("Speedup factor = %.2f\n", speedup);
    if (difference != 0)
        printf("Reference and optimized implementations do not match\n");

    //print(C2, n);

    free(A);
    free(B);
    free(C1);
    free(C2);
    return 0;
}

【问题讨论】:

  • 看来您需要使用 OpenMP 或 OpenCL。 IE。在 GPU 上使用并行执行(或在 CPU 上进行矢量化)。见viennacl
  • 这取决于各种因素,例如矩阵的大小以及您是否在计算方面定义“快速”(考虑并行性、SIMD、循环阻塞等,这可能在很大程度上取决于您的目标架构)或渐近方式(例如,考虑Strassen's algorithm )。你能提供更多信息吗?
  • 我必须在第一个函数中做一些效率算法,例如 Eigen 或 uBLAS。
  • 我应该把每个元素分成字节吗?
  • 再提一句:您的基准测试可能由于缓存效应而受到污染。在对同一数据集(在您的情况下为矩阵)执行时间测量之前,您应该尝试驱逐缓存中存在的任何数据。对于严格的基准测试,您应该坚持使用 Google Benchmarking Library 等库,但我承认这有点离题了。

标签: c++ matrix-multiplication


【解决方案1】:

优化矩阵-矩阵乘法需要仔细注意一些问题:

  • 首先,您需要能够使用向量指令。只有向量指令可以访问架构中固有的并行性。因此,您的编译器需要能够自动映射到向量指令,或者您必须手动执行,例如通过调用 AVX-2 指令的向量内在库(对于 x86 架构)。

    李>
  • 接下来,您需要特别注意内存层次结构。如果你不这样做,你的表现很容易下降到峰值的 5% 以下。

  • 一旦你做对了,你就有希望将计算分解成足够小的计算块,你也可以通过 OpenMP 或 pthread 进行并行化。

可以在http://www.cs.utexas.edu/users/flame/laff/pfhp/LAFF-On-PfHP.html 找到一份详细说明所需内容的文档。 (这在很大程度上是一项正在进行的工作。)最后,您将拥有一个接近高性能库(如英特尔的数学内核库 (MKL) 或类似 BLAS 的库实例化)所获得的性能的实现软件 (BLIS)。

(实际上,您还可以有效地结合 Strassen 的算法。但这是另一个故事,在这些笔记的第 3.5.3 单元中讲述。)

您可能会发现以下相关主题:How does BLAS get such extreme performance?

【讨论】:

    【解决方案2】:

    您可以尝试StrassenCoppersmith-Winograd 之类的算法,这也是一个不错的example。 或者尝试并行计算,如 future::taskstd::thread

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 2015-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多