Интеграция CUDA в приложение C++ для использования существующего класса C++


У меня есть существующее приложение, которое использует класс C++, оболочку C++ и код FORTRAN для вычислительно интенсивных частей приложения. Я хотел бы реализовать части FORTRAN в CUDA, чтобы воспользоваться преимуществами распараллеливания, но я все равно хотел бы получить доступ к некоторым подпрограммам, поэтому мне нужно связать код CUDA, C++ и FORTRAN.

У меня есть три вопроса: 1. Как правильно связать все объектные файлы с терминалом Linux и с файлом Makefile (входит в комплект ниже)? 2. Как правильно ссылаться на функцию CUDA в заголовке класса без путаницы в распознавании компилятором кода устройства и Хоста? 3. Является ли передача класса в CUDA такой же, как передача класса в любой другой внешний код C?

Примечание: Я не включил полный код (некоторые из них довольно длинные), за исключением файла Makefile. Если мне нужно включить больше, пожалуйста, дайте мне знать.

.H file

#ifndef _DGCPM_H_
#define _DGCPM_H_

extern "C"{

#include <string.h>
#include <zlib.h>
#include <math.h>

}

/* Prototypes of Fortran subroutines */
 extern "C" {
  void initialize_(float *2Darray);
  void advance_(float *2Darray);
  //Want "advance" to be implemented in CUDA
}

/* Proper prototype of CUDA call? */
//extern "C" void cudaadvance(float *2Darray);

class DGCPM{

public:
  DGCPM(); /* Initialized with defaults setup */
  ~DGCPM(); /* Free memory */

  void advance(float dT); /* Advance model dT seconds */

private:

  float **2Darray;
  void initialize(float **2Darray);

};

#endif

.C обертка

#include "../include/DGCPM.h"

DGCPM::DGCPM(){

  initialize();
}


void DGCPM::advance(float dT){

  advance_(2Darray[0]);
}

Главная.C file

#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>

#include "../include/DGCPM.h"

int main(){

  class DGCPM *model;
  model=new class DGCPM();

//Write data to class from a file, then

  for(int i=0;i<200;i++){
    printf("%dn",i);
    model->advance(3600);
    //write model state to file;
  }

 //Close file

  return 0;
}

Makefile (Примечание: "ПБО" - это Фортран код)

INSTALLDIR=../../lib/

FLAGS=-Wall -g -I ../../amj/include
CFLAGS=$(FLAGS)
CPPFLAGS=$(FLAGS)
FFLAGS=$(FLAGS)

CPP=g++
CC=gcc
FC=g77

PBO_PATH=../ober/for/
VPATH=$(PBO_PATH)

DGCPM_OBJ=DGCPM.o pbo.o
TESTDGCPM_OBJ=testDGCPM.o DGCPM.o pbo.o

ALL_OBJ=$(TESTDGCPM_OBJ)

install: all
    mkdir -p $(INSTALLDIR)
    cp libDGCPM.a $(INSTALLDIR)

all: libDGCPM.a testDGCPM

libDGCPM.a: $(DGCPM_OBJ)
    ar rc $@ $^

testDGCPM: $(TESTDGCPM_OBJ)
    $(CPP) -o $@ $^ -L ../../amj/lib -lamjMemory -lg2c -lz

clean: 
    - rm $(ALL_OBJ)
    - rm $(INSTALLDIR)/libDGCPM.a
2 5

2 ответа:

В настоящее время у вас нет кода CUDA, поэтому я не могу дать достаточно подробностей.

Для вашего Qs:

  1. связывание объектных файлов, в том числе технологии CUDA код требует драйвер компилятора nvcc. Вы можете сначала скомпилировать файлы кода с помощью отдельных компиляторов, т. е. gcc для .c, g++ ибо .cpp, g77 для .f и nvcc для .cu. Затем вы можете использовать nvcc для связывания всех объектных файлов .o;
  2. код узла и устройства явно объявляется в файле. cu с помощью __host__ и __device__. Вы несете ответственность за то, чтобы не вызывать код устройства из другого кода хоста;
  3. Почему вы передаете класс CUDA? Если вы хотите заменить код fortran на CUDA, вам нужно только вызвать функции CUDA в классе-оболочке C++, и вызов функций API CUDA использует ту же грамматику, что и вызов функций c++.
Вот пример из моего проекта. Исполняемый файл построен с помощью 1 .cu, 1 .cpp, несколько внешних .a, а также некоторые .so. Для .cpp I используйте компилятор Intel icpc, отличный от стандартного g++. Обратите внимание, что мой main() находится в файле .cu.
# Compile   : bin.cu/b-rbm-gpu.cu
nvcc -ftz true -ccbin icpc -Xcompiler "-Wall -Wno-long-long -ansi -pedantic -ansi-alias -parallel -fopenmp -openmp-link=static -static-intel -wd10237" -O3 -Xcompiler "-O3"   -gencode arch=compute_20,code=sm_20  -Ilib -c -o bin.cu/b-rbm-gpu.o bin.cu/b-rbm-gpu.cu
# Compile   : lib/KTiming.cpp
icpc -Wall -Wno-long-long -ansi -pedantic -ansi-alias -parallel -fopenmp -openmp-link=static -static-intel -wd10237 -O3  -MMD -Ilib -c -o lib/KTiming.o lib/KTiming.cpp
# Link  : bin.cu/b-rbm-gpu
nvcc -ftz true -ccbin icpc -Xcompiler "-Wall -Wno-long-long -ansi -pedantic -ansi-alias -parallel -fopenmp -openmp-link=static -static-intel -wd10237" -O3 -Xcompiler "-O3"  -Ilib -Llib bin.cu/b-rbm-gpu.o lib/KTiming.o -lpthread -lm /opt/intel/composer_xe_2013.1.117/mkl/lib/intel64/libmkl_intel_lp64.a /opt/intel/composer_xe_2013.1.117/mkl/lib/intel64/libmkl_intel_thread.a /opt/intel/composer_xe_2013.1.117/mkl/lib/intel64/libmkl_core.a /opt/intel/composer_xe_2013.1.117/mkl/lib/intel64/libmkl_core.a -lcublas -lcurand -lcusparse -o bin.cu/b-rbm-gpu

Вот решение. Чтобы использовать код CUDA, я ссылаюсь на него, например,

extern "C" void myfunction_(void)

В заголовочном файле я добавляю

void myfunction_(void);

В прототипах extern "C". В публичных функциях класса I добавлено

void mycudafunction(void);

В оболочке C++ я добавляю

void DGCPM::mycudafunction(){
 myfunction_();
}

Теперь я могу вызвать "myfunction" из основной программы с этим типом синтаксиса

model = new class DGCPM();
model->mycudafunction();

Я изменил свой Makefile, добавив myfunction.o ко всем моим объектам и добавление

-L /usr/local/cuda/lib -lcuda -lcudart 

Ко всем мои инструкции по связыванию.

Для компиляции создайте объектный файл CUDA (myfunction.o), и ссылка, я набираю это в терминале:

nvcc -c myfunction.cu
make

Вот модифицированный код:

.H file

#ifndef _DGCPM_H_
#define _DGCPM_H_

extern "C"{

#include <string.h>
#include <zlib.h>
#include <math.h>

}

/* Prototypes of Fortran subroutines */
 extern "C" {
  void initialize_(float *2Darray);
  void advance_(float *2Darray);
  /*CUDA prototype, can be changed to "cudaadvance" or the like*/
  void myfunction_(void);

}

class DGCPM{

public:
  DGCPM(); /* Initialized with defaults setup */
  ~DGCPM(); /* Free memory */

  void advance(float dT); /* Advance model dT seconds */
  void mycudafunction(void); 
private:

  float **2Darray;
  void initialize(float **2Darray);

};

#endif

.C Обертка

#include "../include/DGCPM.h"

DGCPM::DGCPM(){

  initialize();
}


void DGCPM::advance(float dT){

  advance_(2Darray[0]);
}

void DGCPM::mycudafunction(){
  myfunction_();
}

Главная.C file

#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>

#include "../include/DGCPM.h"

int main(){

  class DGCPM *model;
  model=new class DGCPM();

//Write data to class from a file, then

  for(int i=0;i<200;i++){
    printf("%d\n",i);
    model->mycudafunction();
    model->advance(3600);
    //write model state to file;
  }

 //Close file

  return 0;
}

Makefile

INSTALLDIR=../../lib/

FLAGS=-Wall -g -I ../../amj/include
CFLAGS=$(FLAGS)
CPPFLAGS=$(FLAGS)
FFLAGS=$(FLAGS)

CPP=g++
CC=gcc
FC=g77

PBO_PATH=../ober/for/
VPATH=$(PBO_PATH)

DGCPM_OBJ=DGCPM.o pbo.o myfunction.o
TESTDGCPM_OBJ=testDGCPM.o DGCPM.o pbo.o myfunction.o

ALL_OBJ=$(TESTDGCPM_OBJ)

install: all
    mkdir -p $(INSTALLDIR)
    cp libDGCPM.a $(INSTALLDIR)

all: libDGCPM.a testDGCPM

libDGCPM.a: $(DGCPM_OBJ)
    ar rc $@ $^

testDGCPM: $(TESTDGCPM_OBJ)
    $(CPP) -o $@ $^ -L ../../amj/lib -lamjMemory -lg2c -lz -L /usr/local/cuda/lib -lcuda -lcudart

clean: 
    - rm $(ALL_OBJ)
    - rm $(INSTALLDIR)/libDGCPM.a
Вот простая программа CUDA, которую я использовал для тестирования.
#include <stdio.h>

__global__ void kernel( void ) {

}

extern "C" void myfunction_(void) {

    kernel<<<1,1>>>();
    printf( "Hello, World!\n" );
    return;


}