2013-07-19 11 views
5

Mam istniejącą aplikację, która używa klasy C++, opakowania C++ i kodu FORTRAN dla intensywnie obliczeniowych części aplikacji. Chciałbym zaimplementować części FORTRAN w CUDA, aby skorzystać z równoległości, ale nadal chciałbym uzyskać dostęp do niektórych podprogramów, więc muszę połączyć kod CUDA, C++ i FORTRAN.Integracja CUDA z aplikacją C++ w celu użycia istniejącej klasy C++

Mam trzy pytania: 1. W jaki sposób poprawnie połączyć wszystkie pliki obiektów za pomocą terminala linuksowego i pliku Makefile (dołączonego poniżej)? 2. Jaki jest właściwy sposób odwoływania się do funkcji CUDA w nagłówku klasy, nie myląc rozpoznawania przez kompilator urządzenia i kodu hosta? 3. Czy przekazywanie klasy do CUDA jest podobne do przekazywania klasy innym zewnętrznym kodom C?

Uwaga: Nie zawierałem pełnego kodu (niektóre są dość długie) z wyjątkiem pliku Makefile. Jeśli potrzebuję dodać więcej, proszę dać mi znać.

.h plików

#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 wrapper

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

DGCPM::DGCPM(){ 

    initialize(); 
} 


void DGCPM::advance(float dT){ 

    advance_(2Darray[0]); 
} 

plik main.c

#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->advance(3600); 
    //write model state to file; 
    } 

//Close file 

    return 0; 
} 

Makefile (uwaga: "PBO" oznacza kod FORTRAN)

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 [email protected] $^ 

testDGCPM: $(TESTDGCPM_OBJ) 
    $(CPP) -o [email protected] $^ -L ../../amj/lib -lamjMemory -lg2c -lz 

clean: 
    - rm $(ALL_OBJ) 
    - rm $(INSTALLDIR)/libDGCPM.a 
+0

Odnośnie trzeciego pytania, miałem starsze kodu napisane w C++ i przy użyciu klas. Udało mi się przenieść kod do 'CUDA', zmieniając wszystkie operacje procesora (wykonania, ruchy pamięci itp.) Na operacje GPU (jądra,' cudaMemcpy's itp.). – JackOLantern

Odpowiedz

2

Oto rozwiązanie.Aby użyć kodu CUDA, odwołać go z, na przykład,

extern "C" void myfunction_(void) 

W pliku nagłówkowym, dodaję

void myfunction_(void); 

w extern "C" prototypów. W funkcjach publicznych klasy I dodaje

void mycudafunction(void); 

w folderze C++ wrapper, dodaję

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

mogę teraz nazwać "myfunction" z programu głównego z tego typu składni

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

Zmodyfikowałem plik Makefile, dodając plik myfunction.o do wszystkich moich obiektów i dodając

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

do wszystkich moich instrukcji dotyczących łączenia.

Aby skompilować, utwórz plik CUDA obiektu (myfunction.o), oraz link, wpisuję to w terminalu:

nvcc -c myfunction.cu 
make 

Oto zmodyfikowany kod:

.h plików

#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 Wrapper

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

DGCPM::DGCPM(){ 

    initialize(); 
} 


void DGCPM::advance(float dT){ 

    advance_(2Darray[0]); 
} 

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

main.c plik

#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 [email protected] $^ 

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

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

Oto prosty program CUDA kiedyś przetestować.

#include <stdio.h> 

__global__ void kernel(void) { 

} 

extern "C" void myfunction_(void) { 

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


} 
2

Obecnie nie masz kodu CUDA, więc nie mogę podać wystarczająco dużo szczegółów.

Dla firmy Q:

  1. Łączenie plików obiektowych tym kod CUDA wymaga nvcc compiler driver. Można najpierw skompilować pliki kodu z poszczególnych kompilatorów, tj gcc dla .c, g++ dla .cpp, g77 dla .f i nvcc dla .cu. Następnie możesz użyć nvcc, aby połączyć wszystkie pliki obiektów .o;
  2. Kod hosta i urządzenia są jawnie zadeklarowane w pliku .cu z __host__ i __device__. Twoim obowiązkiem nie jest wywoływanie kodu urządzenia z innego kodu hosta;
  3. Dlaczego przekazujesz klasę do CUDA? Jeśli chcesz zastąpić swój kod Fortrana CUDA, wystarczy wywołać funkcje CUDA w twojej klasie C++, a wywoływanie funkcji API CUDA używa tej samej gramatyki, co wywoływanie funkcji C++.

Oto przykład z mojego projektu. Plik wykonywalny jest zbudowany z 1 .cu, 1 .cpp, kilku zewnętrznych .a, a także niektórych .so. Dla .cpp używam kompilatora Intela icpc innego niż domyślny g++. Pamiętaj, że moja main() znajduje się w pliku .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 
+0

Dzięki za odpowiedź, Eric. Pomógł mi dojść do rozwiązania. Załączam dokumentację do mojego rozwiązania w mojej własnej odpowiedzi. –