CUDA代码与Fortran和C代码(混合语言编程(相结合的方法是什么?Fortran 代码调用一个 C 函数,而 C 函数又调用 CUDA 内核。例如。
Fortran功能:
if(flag.eq.1) call c_func
C函数:
void c_func()
{
/* copy data to device
....
cuda_kernel<<< kernel parameters>>>();
/* copy data from device to Host
........
}
编译此类代码的方法是什么?
有很多可能的方法。 但是按照您给出的示例,它应该相当简单。
该任务可以分为两部分:
- 如何从 Fortran 调用 C 函数
- 如何从 C 调用 CUDA 函数
我认为您的问题可能围绕第一部分,因此它并不是真正的 CUDA 特定。 当然,对于第二部分,cuda 标签上有很多示例,以及 cuda 示例代码和编程指南。
一种可能有助于简化第一部分的方法是使用内置于许多当前 fortran 发行版中的ISO_C_BINDING
内置模块。 此模块定义了许多类型,这些类型对于在 C 和 Fortran 之间传递数据很有用。
然后,您可以创建一个INTERFACE
块来定义您希望从 fortran 调用的 C 函数的参数。 这是一个工作示例:
$ cat cuda_test.f90
!=======================================================================================================================
!Interface to cuda C functions
!=======================================================================================================================
module cuda_test
use iso_c_binding
interface
!
integer(c_int) function cudatestfunc(idata, isize) bind(C, name="cudatestfunc")
use iso_c_binding
implicit none
type(c_ptr),value :: idata
integer(c_int),value :: isize
end function cudatestfunc
!
end interface
end module cuda_test
!=======================================================================================================================
program main
!=======================================================================================================================
use iso_c_binding
use cuda_test
type(c_ptr) :: mydata
integer*4, target :: mysize,myresult
integer*4,dimension(:),allocatable,target :: darray
mysize = 100
allocate(darray(mysize))
darray = (/ (1, I = 1, mysize) /)
mydata = c_loc(darray)
myresult = cudatestfunc(mydata, mysize)
write (*, '(A, I10)') " result: ", myresult
write (*,*)
end program main
$ cat cuda_test.cu
#include <stdio.h>
#define cudaCheckErrors(msg)
do {
cudaError_t __err = cudaGetLastError();
if (__err != cudaSuccess) {
fprintf(stderr, "Fatal error: %s (%s at %s:%d)n",
msg, cudaGetErrorString(__err),
__FILE__, __LINE__);
fprintf(stderr, "*** FAILED - ABORTINGn");
exit(1);
}
} while (0)
__global__ void testkernel(int *data, int size){
for (int i = 1; i < size; i++) data[0] += data[i];
}
extern "C" {
int cudatestfunc(int *data, int size){
int *d_data;
cudaMalloc(&d_data, size*sizeof(int));
cudaMemcpy(d_data, data, size*sizeof(int), cudaMemcpyHostToDevice);
testkernel<<<1,1>>>(d_data, size);
int result;
cudaMemcpy(&result, d_data, sizeof(int), cudaMemcpyDeviceToHost);
cudaCheckErrors("cuda error");
return result;
}
}
$ gfortran -c cuda_test.f90 -o cuda_testf.o
$ nvcc -c cuda_test.cu -o cuda_testc.o
$ gfortran cuda_testc.o cuda_testf.o -o cuda_test -L/usr/local/cuda/lib64 -lcudart -lstdc++
$ ./cuda_test
result: 100
$
(在RHEL 6.2,GNU 4.4.7,CUDA 7.0上测试(
注释/其他选项:
如果您只需要调用 CUDA 运行时 API 函数,则可以直接从 fortran 调用这些函数,而无需任何 C/C++ 文件(如果您创建自己的绑定(。 这里的例子。
如果您只需要调用 CUSPARSE 或 CUBLAS 库函数,则可以为您创建一些绑定,这些绑定包含在 CUDA 发行版中。 默认情况下,Linux 上的这些绑定安装在
/usr/local/cuda/src
. cublas 绑定的一个工作示例包含在 cublas 文档中。如果您需要直接从 fortran 调用其他 CUDA 库函数,创建自己的绑定并不是非常困难。 这里有一个工作示例,用于CUSOLVER中的一组简单操作。
您也可以直接编写 CUDA Fortran 代码。(下面是一个示例。 这需要PGI的CUDA Fortran编译器。
你也可以编写OpenACC Fortran代码。 这需要可用的OpenACC编译器之一,例如PGI的编译器。 PGI免费学术使用或试用版可在此处获得