混合语言CUDA编程

时间:2016-02-02 10:22:36

标签: c cuda fortran

将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
  ........
}

编译此类代码的方法是什么?

1 个答案:

答案 0 :(得分:3)

我确定有很多可能的方法。但是按照你给出的例子,这应该是相当简单的。

任务可以分为两部分:

  1. 如何从Fortran调用C函数
  2. 如何从C
  3. 调用CUDA函数

    我认为你的问题可能围绕着第一部分,因此它并不是特定的CUDA。对于第二部分,cuda标签上有很多例子,以及cuda sample codesprogramming 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上测试)

    备注/其他选项:

    1. 如果您只需要调用CUDA运行时API函数,则可以直接从fortran调用它们而无需任何C / C ++文件(如果您创建自己的绑定)。示例here

    2. 如果您只需要调用CUSPARSE或CUBLAS库函数,则会为您创建一些CUDA分发包含的绑定。默认情况下,这些绑定安装在/usr/local/cuda/src。 Cublas绑定的一个有效例子包含在the cublas documentation

    3. 如果您需要直接从fortran调用其他CUDA库函数,那么创建自己的绑定并不是非常困难。对于CUSOLVER中的一组简单操作,一个有效的例子是here

    4. 您也可以直接编写CUDA Fortran代码。 (Here就是一个例子。)这需要CUDA Fortran compiler from PGI

    5. 您还可以编写OpenACC Fortran代码。这需要一个可用的OpenACC编译器,例如来自PGI的编译器。可以使用PGI免费学术用途或试用版here