【问题标题】:How do I use the GPU available with OpenMP?如何使用 OpenMP 提供的 GPU?
【发布时间】:2017-11-24 22:00:05
【问题描述】:

我正在尝试使用 OpenMP 在 GPU 上运行一些代码,但我没有成功。在我的代码中,我正在使用 for 循环执行矩阵乘法:一次使用 OpenMP 杂注标记,一次不使用。 (这样我可以比较执行时间。)在第一个循环之后,我调用omp_get_num_devices()(这是我的主要测试,看看我是否真的连接到 GPU。)无论我尝试什么, omp_get_num_devices() 总是返回 0。

我使用的计算机有两个 NVIDIA Tesla K40M GPU。 CUDA 7.0 和 CUDA 7.5 作为模块在计算机上可用,并且 CUDA 7.5 模块通常处于活动状态。 gcc 4.9.3、5.1.0 和 7.1.0 都可作为模块使用,gcc 7.1.0 模块通常处于活动状态。我正在用$ g++ -fopenmp -omptargets=nvptx64sm_35-nvidia-linux ParallelExperimenting.cpp -o ParallelExperimenting 编译我的代码。我已经使用 CPU 成功地并行化了 OpenMP 代码,但没有使用 GPU。

我的主要目标是让 omp_get_num_devices() 返回 2,以证明我可以通过 OpenMP 检测和使用 GPU。我将不胜感激在此获得的任何帮助。

这是我用来检查 GPU 是否正确使用的代码:

#include <omp.h>
#include <fstream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <iomanip>
#include <cstdio>
#include <stdlib.h>
#include <iostream>
#include <time.h>
using namespace std;

double A [501][501];
double B [501][501];
double C [501][501][501];
double D [501][501];
double E [501][501];
double F [501][501][501];
double dummyvar;
int Mapped [501];

int main() {
    int i, j, k, l, N, StallerGPU, StallerCPU;

    //
    N = 500;

    // Variables merely uses to make the execution take longer and to
    //   exaggurate the difference in performance between first and second
    //   calculation
    StallerGPU = 200;
    StallerCPU = 200;

    std::cout << " N = " << N << "\n";
    // generate matrix to be used in first calculation
    for (i=0; i<N; i++) {
        for (k=0; k<N; k++) {
            if (i == k) {
                A[i][k] = i+1;
            } else {
                A[i][k] = i * k / N;
            }
        }
    }
    // generate other matrix to be used for the first calculation
    for (k=0; k<N; k++) {
        for (j=0; j<N; j++) {
            B[k][j] = 2*(N-1)-k-j;
        }
    }

//    Slightly adjusted matrices for second calculation
    for (i=0; i<N; i++) {
        for (k=0; k<N; k++) {
            if (i == k) {
                D[i][k] = i+2;
            } else {
                D[i][k] = i * k / N - 1;
            }
        }
    }

    for (k=0; k<N; k++) {
        for (j=0; j<N; j++) {
            E[k][j] = 2*(N+1)-k-j;
        }
    }

    dummyvar = 0;

    //Run the multiplication in parallel using GPUs

    double diff;
    time_t time1;
    time1 = time( NULL ); // CPU time counter
    cout << endl << " GPU section begins at " << ctime(&time1) << endl;

        //    This pragma is frequently changed to try different tags
        #pragma omp for collapse(4) private(i, j, k, l)

        for (i=0; i<N; i++) {
//            Mapped[i] = omp_is_initial_device();
            for (j=0; j<N; j++) {
                for (k=0; k<N; k++) {
                    for(l = 0; l < StallerGPU; l++ ) {
                        C[i][j][k] = A[i][k] * B[k][j] ;
                        dummyvar += A[i][k] * B[k][j] * (l + 1);
                    }
                }
//            cout << " i " << i << endl;
            }
        }


    //record the time it took to run the multiplication    
    time_t time2 = time( NULL );
    cout << " number of devices: " << omp_get_num_devices() << endl;
    cout << " dummy variable: " << dummyvar << endl;

    float cpumin = difftime(time2,time1);
    diff = difftime(time2,time1);
    cout << " stopping at delta GPU time: " << cpumin << endl; 
    cout << " terminating at " << ctime(&time2) << endl;
    cout << " GPU time elasped " << diff << " s" << endl;
    cout << endl;

    dummyvar = 0;
    time_t time3 = time( NULL );
    cout << endl << " CPU section begins at " << ctime(&time3) << endl;
//    #pragma omp single
    for (i=0; i<N; i++) {
        for (j=0; j<N; j++) {
            for (k=0; k<N; k++) {
                for (int l=0; l<StallerCPU; l++) {
                    F[i][j][k] = D[i][k] * E[k][j];
                    dummyvar += D[i][k] * E[k][j] * (l - 1);
                }
            }
        }
    }
    // the sum to complete the matrix calculation is left out here, but would
    // only be used to check if the result of the calculation is correct

    time_t time4 = time( NULL );
    cpumin = difftime(time4,time3);
    diff = difftime(time4,time3);
    cout << " dummy variable: " << dummyvar << endl;
    cout << " stopping at delta CPU time: " << cpumin << endl; 
    cout << " terminating at " << ctime(&time4) << endl;
    cout << " CPU time elasped " << diff << " s" << endl;
    //Compare the time it took to confirm that we actually used GPUs to parallelize.
}

这是运行 deviceQuery 示例 CUDA 代码的结果。

./deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 2 CUDA Capable device(s)

Device 0: "Tesla K40m"
  CUDA Driver Version / Runtime Version          7.5 / 7.5
  CUDA Capability Major/Minor version number:    3.5
  Total amount of global memory:                 11520 MBytes (12079136768 bytes)
  (15) Multiprocessors, (192) CUDA Cores/MP:     2880 CUDA Cores
  GPU Max Clock rate:                            745 MHz (0.75 GHz)
  Memory Clock rate:                             3004 Mhz
  Memory Bus Width:                              384-bit
  L2 Cache Size:                                 1572864 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
  Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  2048
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 2 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Enabled
  Device supports Unified Addressing (UVA):      Yes
  Device PCI Domain ID / Bus ID / location ID:   0 / 130 / 0
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

Device 1: "Tesla K40m"
  CUDA Driver Version / Runtime Version          7.5 / 7.5
  CUDA Capability Major/Minor version number:    3.5
  Total amount of global memory:                 11520 MBytes (12079136768 bytes)
  (15) Multiprocessors, (192) CUDA Cores/MP:     2880 CUDA Cores
  GPU Max Clock rate:                            745 MHz (0.75 GHz)
  Memory Clock rate:                             3004 Mhz
  Memory Bus Width:                              384-bit
  L2 Cache Size:                                 1572864 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
  Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  2048
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 2 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Enabled
  Device supports Unified Addressing (UVA):      Yes
  Device PCI Domain ID / Bus ID / location ID:   0 / 131 / 0
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >
> Peer access from Tesla K40m (GPU0) -> Tesla K40m (GPU1) : Yes
> Peer access from Tesla K40m (GPU1) -> Tesla K40m (GPU0) : Yes

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 7.5, CUDA Runtime Version = 7.5, NumDevs = 2, Device0 = Tesla K40m, Device1 = Tesla K40m
Result = PASS

【问题讨论】:

  • 你能上传一个最低限度的工作示例来说明你正在尝试做什么吗?
  • 欢迎来到 Stack Overflow!很遗憾,您的帖子缺少minimal reproducible example。请访问help center 并阅读how do I ask a good question 部分。
  • 我添加了我的测试代码。
  • 您从哪里获得-omptargets=?这是一个 clang 的选项,而不是 gcc 的。
  • 我猜你的 GCC 不支持卸载到 GPU。实际上,此功能需要安装 2 个不同的 GCC(gcc 目标主机和 gcc 目标 gpu)。我不确定你的发行版中是否有这样的包,我更喜欢从here 描述的源代码构建。

标签: c++ gcc openmp gpgpu offloading


【解决方案1】:

也许我走错了方向。但我想帮忙,

因为我曾经在使用 GPU 时遇到过奇怪的情况。

你需要在linux的“视频”组,才能使用GPU。

否则从 GPU 返回的结果全部为 0。

所以我建议你运行一个示例 CUDA 代码来检查你是否处于我曾经被卡住的情况。

这很奇怪。我不确定我是否正确地描述了它。 希望能帮助到你。


据此:https://wiki.gentoo.org/wiki/NVidia/nvidia-drivers

需要将需要访问视频卡的用户添加到 视频组

【讨论】:

  • 为什么不提供链接参考?这听起来是一个很好的答案,而且听起来会更好
  • 我将添加运行 deviceQuery 示例 CUDA 代码的结果。
  • @MarkSetchell 谢谢。我不知道 AMD GPU 也需要这个。
【解决方案2】:

我可能错了,但我认为您需要对发布的代码进行一些更正(也许您已经知道了)。要使用 OpenMP 在 GPU 目标上实际运行,您需要替换:

#pragma omp for collapse(4) private(i, j, k, l)

#pragma omp target teams distribute parallel for collapse(4) private(i, j, k, l)

您可以通过使用“nvprof”分析您的可执行文件来验证内核是否实际在 GPU 上运行。它应该显示在 GPU 上执行的内核。您还可以使用“num_teams”和“thread_limit”子句更改目标区域中的团队和线程数,您应该会在您的个人资料中看到相应的更改。

要以编程方式实际检查目标区域是否在目标设备上运行,我使用“omp_is_initial_device()”调用,当从加速器调用时返回 0。这是一个例子:

int A[1] = {-1};
#pragma omp target
{
  A[0] = omp_is_initial_device();
}

if (!A[0]) {
  printf("Able to use offloading!\n");
}

【讨论】:

  • 我尝试按照您的建议使用nvprof 对其进行分析。程序完成执行后,我收到错误======== Warning: No CUDA application was profiled, exiting。当我添加omp_is_initial_device() 时,它每次都返回一个 1。
  • 这似乎强烈表明您的内核正在 CPU 上运行。正如 Ilya 提到的,您可能需要编译 gcc 并支持 gpu。
  • 为什么需要使用一个元素组成的数组,而不仅仅是一个简单的整数?我试过你的代码,它只适用于数组,但我不明白为什么。
  • 我想通了。标量值默认为firstprivate。所以指针是有效共享的。 #pragma omp target defaultmap(tofrom:scalar) 允许共享标量。
【解决方案3】:

GCC 4.9.3 和 5.1.0 绝对不支持将 OpenMP 卸载到 GPU。 GCC 7.1.0 确实支持它,但是应该使用特殊的配置选项构建它,as described here

【讨论】:

    猜你喜欢
    • 2017-10-18
    • 2021-10-10
    • 1970-01-01
    • 2020-01-07
    • 2021-12-17
    • 2013-05-06
    • 2017-12-19
    • 2018-09-06
    • 1970-01-01
    相关资源
    最近更新 更多