将CUDA代码与Fortran和C代码(混合语言编程)结合使用的方法是什么? Fortran代码调用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
........
}
编译此类代码的方法是什么?
答案 0 :(得分:3)
我确定有很多可能的方法。但是按照你给出的例子,这应该是相当简单的。
任务可以分为两部分:
我认为你的问题可能围绕着第一部分,因此它并不是特定的CUDA。对于第二部分,cuda标签上有很多例子,以及cuda sample codes和programming guide。
一种可能有助于简化第一部分的方法是使用内置于许多当前fortran发行版中的ISO_C_BINDING
builtin module。该模块定义了许多类型,这些类型对于在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 - ABORTING\n"); \
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 ++文件(如果您创建自己的绑定)。示例here。
如果您只需要调用CUSPARSE或CUBLAS库函数,则会为您创建一些CUDA分发包含的绑定。默认情况下,这些绑定安装在/usr/local/cuda/src
。 Cublas绑定的一个有效例子包含在the cublas documentation。
如果您需要直接从fortran调用其他CUDA库函数,那么创建自己的绑定并不是非常困难。对于CUSOLVER中的一组简单操作,一个有效的例子是here。
您也可以直接编写CUDA Fortran代码。 (Here就是一个例子。)这需要CUDA Fortran compiler from PGI。
您还可以编写OpenACC Fortran代码。这需要一个可用的OpenACC编译器,例如来自PGI的编译器。可以使用PGI免费学术用途或试用版here