使用 OpenACC 和 cublasDgemv 将 g++ 与 pgi 编译代码链接时出现内存错误
Memory error when linking g++ with pgi compiled code using OpenACC and cublasDgemv
为了在使用 g++ 编译的应用程序中将我的 GPU 与 OpenACC 和 cublas 一起使用,我设置了一个小的测试示例。为此,我创建了文件:
- main.cpp
- pgiCudaCode.h
- pgiCudaCode.cpp
我的测试系统是 Ubuntu 18.04 linux,g++ 版本 7.5.0 和 pgc++ 版本 19.10-0,带有 Nvidia GTX1070 卡。
文件 pgiCudaCode.cpp 对一般矩阵向量与 openACC 和 cublas 相乘有一些实现。此文件符合 PGI 编译器和命令:
pgc++ -fast -Minfo=opt -ta:tesla:cc60,managed,nordc -Mcudalib=cublas -Minfo=accel -fPIC pgiCudaCode.cpp -c pgiCudaCode.o
我确实使用选项 nordc 以便使用 g++ 进行操作。
主文件已用 g++ 编译和链接:
g++ -fPIC pgiCudaCode.o -L/opt/pgi/linux86-64/19.10/lib/ -laccapi -laccg -laccn -laccg2 -lpgiman -ldl -lcudadevice -lcudapgi -lomp -lnuma -lpthread -lnspgc -lpgc -lm -lgcc -lc -lgcc -lpgmath -lblas -lpgatm -lpgkomp -L/opt/pgi/linux86-64/2019/cuda/10.1/lib64/ -lcublas -lcublasLt -lcudart main.cpp -o mainGCC
在我的 Ubuntu 18.04
上设置这些导出后
export LD_LIBRARY_PATH="/opt/pgi/linux86-64/19.10/lib/:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="/opt/pgi/linux86-64/2019/cuda/10.1/lib64/:$LD_LIBRARY_PATH"
我可以 运行 可执行的 mainGCC 并获得以下输出:
./mainGCC
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
Failing in Thread:1
call to cuMemcpyDtoHAsync returned error 700: Illegal address during kernel execution
Failing in Thread:1
call to cuMemFreeHost returned error 700: Illegal address during kernel execution
使用 pgi 编译器链接和编译 main.cpp 时,我没有收到此错误:
pgc++ -fast -Minfo=opt -ta:tesla:cc60,managed,nordc -Mcudalib=cublas -Minfo=accel -fPIC pgiCudaCode.o main.cpp -o mainPGI
这里mainPGI的输出是正确的:
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
4
6
所以有趣的部分是:
- 在函数 matmulPureOpenACC 中使用 g++ 分配的内存不会导致任何问题,而使用 cublas 的函数 matmul 会出错。
- 当你在函数matmul_internAlloc中使用pgi分配的内存时,cublas可以用g++操作。
这引出了我的问题:
如何在函数 matmul 中使用 g++ 分配内存来防止此错误?
这里是对应的.cpp和.h文件。
main.cpp:
#include <iostream>
#include "pgiCudaCode.h"
void printVec(int N, double* vec)
{
for(int i = 0; i < N; i++)
{
std::cout << vec[i] << std::endl;
}
}
void printMatrix(int N, double* matr)
{
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
std::cout << '\t' << matr[i + j * N];
}
std::cout << std::endl;
}
}
int main()
{
int N = 2;
double* vec1 = new double[N];
vec1[0] = 1.0;
vec1[1] = 1.0;
double* vec2 = new double[N];
vec2[0] = 0.0;
vec2[1] = 0.0;
double* matr = new double[N*N];
matr[0] = 1.0;
matr[1] = 2.0;
matr[2] = 3.0;
matr[3] = 4.0;
std::cout << "Vector 1:" << std::endl;
printVec(N, vec1);
std::cout << std::endl;
std::cout << "Matrix:" << std::endl;
printMatrix(N, matr);
std::cout << std::endl;
std::cout << "matrix*vec pure openACC:" << std::endl;
matmulPureOpenACC(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
vec2[0] = 0.0;
vec2[1] = 0.0;
std::cout << "matrix*vec cublas with internal allocation:" << std::endl;
matmul_internAlloc(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
vec2[0] = 0.0;
vec2[1] = 0.0;
std::cout << "matrix*vec cublas without internal allocation:" << std::endl;
matmul(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
delete [] vec1;
delete [] vec2;
delete [] matr;
return 0;
}
pgiCudaCode.h:
#ifndef PGICUDACODE_H
#define PGICUDACODE_H
bool matmul(int n, const double* matr, const double* b, double* c);
bool matmul_internAlloc(int n, const double* matr, const double* b, double* c);
bool matmulPureOpenACC(int n, const double* matr, const double* b, double* c);
#endif
pgiCudaCode.cpp:
#include <iostream>
#include <cublas_v2.h>
void matmul(int n, const double* matr, const double* b, double* c)
{
#pragma acc data pcopyin(n , matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc host_data use_device(matr, b, c)
{
cublasHandle_t handle;
cublasStatus_t stat = cublasCreate(&handle);
if ( CUBLAS_STATUS_SUCCESS != stat ) {
std::cerr<<"CUBLAS initialization failed"<<std::endl;
}
if ( CUBLAS_STATUS_SUCCESS == stat )
{
const double alpha = 1.0;
const double beta = 1.0;
stat = cublasDgemv_v2(handle, CUBLAS_OP_N, n,n, &alpha, matr, n, b, 1, &beta, c, 1);
if (stat != CUBLAS_STATUS_SUCCESS) {
std::cerr<<"cublasDgemm failed"<<std::endl;
}
}
cublasDestroy(handle);
}
}
}
void matmul_internAlloc(int n2, const double* matr2, const double* b2, double* c2)
{
int n = n2;
double* matr = new double[n*n];
double* b = new double[n];
double* c = new double[n];
std::copy(&matr2[0], &matr2[n*n], &matr[0]);
std::copy(&b2[0], &b2[n], &b[0]);
std::copy(&c2[0], &c2[n], &c[0]);
#pragma acc data pcopyin(n , matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc host_data use_device(matr, b, c)
{
cublasHandle_t handle;
cublasStatus_t stat = cublasCreate(&handle);
if ( CUBLAS_STATUS_SUCCESS != stat ) {
std::cerr<<"CUBLAS initialization failed"<<std::endl;
}
if ( CUBLAS_STATUS_SUCCESS == stat )
{
const double alpha = 1.0;
const double beta = 1.0;
stat = cublasDgemv_v2(handle, CUBLAS_OP_N, n,n, &alpha, matr, n, b, 1, &beta, c, 1);
if (stat != CUBLAS_STATUS_SUCCESS) {
std::cerr<<"cublasDgemm failed"<<std::endl;
}
}
cublasDestroy(handle);
}
}
std::copy(&c[0], &c[n], &c2[0]);
delete [] matr;
delete [] b;
delete [] c;
}
void matmulPureOpenACC(int n, const double* matr, const double* b, double* c)
{
#pragma acc data pcopyin(n, matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc parallel loop
for(int i = 0; i < n; i++)
{
#pragma acc loop seq
for(int j = 0; j < n; j++)
{
c[i] += matr[i + j*n]*b[j];
}
}
}
}
你最好使用 pgc++ 来 link。用 g++ 编译 main.cpp 没问题,但 PGI 编译器在 link 时会隐式包含一些 OpenACC 和 CUDA 互操作性所需的初始化例程。如果没有这个初始化,你会看到像这样的运行时错误。
% pgc++ -fast -ta:tesla:cc70 pgiCudaCode.cpp -c pgiCudaCode.o
pgiCudaCode.cpp:
% g++ -c main.cpp
% pgc++ -fast -ta:tesla:cc70 -Mcudalib=cublas -Mcuda pgiCudaCode.o main.o -o mainGCC
% ./mainGCC
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
4
6
为了在使用 g++ 编译的应用程序中将我的 GPU 与 OpenACC 和 cublas 一起使用,我设置了一个小的测试示例。为此,我创建了文件:
- main.cpp
- pgiCudaCode.h
- pgiCudaCode.cpp
我的测试系统是 Ubuntu 18.04 linux,g++ 版本 7.5.0 和 pgc++ 版本 19.10-0,带有 Nvidia GTX1070 卡。
文件 pgiCudaCode.cpp 对一般矩阵向量与 openACC 和 cublas 相乘有一些实现。此文件符合 PGI 编译器和命令:
pgc++ -fast -Minfo=opt -ta:tesla:cc60,managed,nordc -Mcudalib=cublas -Minfo=accel -fPIC pgiCudaCode.cpp -c pgiCudaCode.o
我确实使用选项 nordc 以便使用 g++ 进行操作。
主文件已用 g++ 编译和链接:
g++ -fPIC pgiCudaCode.o -L/opt/pgi/linux86-64/19.10/lib/ -laccapi -laccg -laccn -laccg2 -lpgiman -ldl -lcudadevice -lcudapgi -lomp -lnuma -lpthread -lnspgc -lpgc -lm -lgcc -lc -lgcc -lpgmath -lblas -lpgatm -lpgkomp -L/opt/pgi/linux86-64/2019/cuda/10.1/lib64/ -lcublas -lcublasLt -lcudart main.cpp -o mainGCC
在我的 Ubuntu 18.04
上设置这些导出后export LD_LIBRARY_PATH="/opt/pgi/linux86-64/19.10/lib/:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="/opt/pgi/linux86-64/2019/cuda/10.1/lib64/:$LD_LIBRARY_PATH"
我可以 运行 可执行的 mainGCC 并获得以下输出:
./mainGCC
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
Failing in Thread:1
call to cuMemcpyDtoHAsync returned error 700: Illegal address during kernel execution
Failing in Thread:1
call to cuMemFreeHost returned error 700: Illegal address during kernel execution
使用 pgi 编译器链接和编译 main.cpp 时,我没有收到此错误:
pgc++ -fast -Minfo=opt -ta:tesla:cc60,managed,nordc -Mcudalib=cublas -Minfo=accel -fPIC pgiCudaCode.o main.cpp -o mainPGI
这里mainPGI的输出是正确的:
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
4
6
所以有趣的部分是:
- 在函数 matmulPureOpenACC 中使用 g++ 分配的内存不会导致任何问题,而使用 cublas 的函数 matmul 会出错。
- 当你在函数matmul_internAlloc中使用pgi分配的内存时,cublas可以用g++操作。
这引出了我的问题:
如何在函数 matmul 中使用 g++ 分配内存来防止此错误?
这里是对应的.cpp和.h文件。
main.cpp:
#include <iostream>
#include "pgiCudaCode.h"
void printVec(int N, double* vec)
{
for(int i = 0; i < N; i++)
{
std::cout << vec[i] << std::endl;
}
}
void printMatrix(int N, double* matr)
{
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
std::cout << '\t' << matr[i + j * N];
}
std::cout << std::endl;
}
}
int main()
{
int N = 2;
double* vec1 = new double[N];
vec1[0] = 1.0;
vec1[1] = 1.0;
double* vec2 = new double[N];
vec2[0] = 0.0;
vec2[1] = 0.0;
double* matr = new double[N*N];
matr[0] = 1.0;
matr[1] = 2.0;
matr[2] = 3.0;
matr[3] = 4.0;
std::cout << "Vector 1:" << std::endl;
printVec(N, vec1);
std::cout << std::endl;
std::cout << "Matrix:" << std::endl;
printMatrix(N, matr);
std::cout << std::endl;
std::cout << "matrix*vec pure openACC:" << std::endl;
matmulPureOpenACC(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
vec2[0] = 0.0;
vec2[1] = 0.0;
std::cout << "matrix*vec cublas with internal allocation:" << std::endl;
matmul_internAlloc(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
vec2[0] = 0.0;
vec2[1] = 0.0;
std::cout << "matrix*vec cublas without internal allocation:" << std::endl;
matmul(N, matr, vec1, vec2);
printVec(N, vec2);
std::cout << std::endl;
delete [] vec1;
delete [] vec2;
delete [] matr;
return 0;
}
pgiCudaCode.h:
#ifndef PGICUDACODE_H
#define PGICUDACODE_H
bool matmul(int n, const double* matr, const double* b, double* c);
bool matmul_internAlloc(int n, const double* matr, const double* b, double* c);
bool matmulPureOpenACC(int n, const double* matr, const double* b, double* c);
#endif
pgiCudaCode.cpp:
#include <iostream>
#include <cublas_v2.h>
void matmul(int n, const double* matr, const double* b, double* c)
{
#pragma acc data pcopyin(n , matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc host_data use_device(matr, b, c)
{
cublasHandle_t handle;
cublasStatus_t stat = cublasCreate(&handle);
if ( CUBLAS_STATUS_SUCCESS != stat ) {
std::cerr<<"CUBLAS initialization failed"<<std::endl;
}
if ( CUBLAS_STATUS_SUCCESS == stat )
{
const double alpha = 1.0;
const double beta = 1.0;
stat = cublasDgemv_v2(handle, CUBLAS_OP_N, n,n, &alpha, matr, n, b, 1, &beta, c, 1);
if (stat != CUBLAS_STATUS_SUCCESS) {
std::cerr<<"cublasDgemm failed"<<std::endl;
}
}
cublasDestroy(handle);
}
}
}
void matmul_internAlloc(int n2, const double* matr2, const double* b2, double* c2)
{
int n = n2;
double* matr = new double[n*n];
double* b = new double[n];
double* c = new double[n];
std::copy(&matr2[0], &matr2[n*n], &matr[0]);
std::copy(&b2[0], &b2[n], &b[0]);
std::copy(&c2[0], &c2[n], &c[0]);
#pragma acc data pcopyin(n , matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc host_data use_device(matr, b, c)
{
cublasHandle_t handle;
cublasStatus_t stat = cublasCreate(&handle);
if ( CUBLAS_STATUS_SUCCESS != stat ) {
std::cerr<<"CUBLAS initialization failed"<<std::endl;
}
if ( CUBLAS_STATUS_SUCCESS == stat )
{
const double alpha = 1.0;
const double beta = 1.0;
stat = cublasDgemv_v2(handle, CUBLAS_OP_N, n,n, &alpha, matr, n, b, 1, &beta, c, 1);
if (stat != CUBLAS_STATUS_SUCCESS) {
std::cerr<<"cublasDgemm failed"<<std::endl;
}
}
cublasDestroy(handle);
}
}
std::copy(&c[0], &c[n], &c2[0]);
delete [] matr;
delete [] b;
delete [] c;
}
void matmulPureOpenACC(int n, const double* matr, const double* b, double* c)
{
#pragma acc data pcopyin(n, matr[0:n*n], b[0:n]) pcopy(c[0:n])
{
#pragma acc parallel loop
for(int i = 0; i < n; i++)
{
#pragma acc loop seq
for(int j = 0; j < n; j++)
{
c[i] += matr[i + j*n]*b[j];
}
}
}
}
你最好使用 pgc++ 来 link。用 g++ 编译 main.cpp 没问题,但 PGI 编译器在 link 时会隐式包含一些 OpenACC 和 CUDA 互操作性所需的初始化例程。如果没有这个初始化,你会看到像这样的运行时错误。
% pgc++ -fast -ta:tesla:cc70 pgiCudaCode.cpp -c pgiCudaCode.o
pgiCudaCode.cpp:
% g++ -c main.cpp
% pgc++ -fast -ta:tesla:cc70 -Mcudalib=cublas -Mcuda pgiCudaCode.o main.o -o mainGCC
% ./mainGCC
Vector 1:
1
1
Matrix:
1 3
2 4
matrix*vec pure openACC:
4
6
matrix*vec cublas with internal allocation:
4
6
matrix*vec cublas without internal allocation:
4
6