Jak rozdzielić kod CUDA na wiele plików

Próbuję oddzielić program CUDA na dwa oddzielne pliki. cu, aby zbliżyć się do napisania prawdziwej aplikacji w C++. Mam prosty mały program, który:

Przydziela pamięć na hoście i urządzeniu.
Inicjalizuje tablicę hosta do szeregu liczb. Kopiuje tablicę hosta do tablicy urządzeń Wyszukuje kwadrat wszystkich elementów w tablicy za pomocą jądra urządzenia Kopiuje tablicę urządzeń z powrotem do tablicy hosta Drukuje wyniki

To działa świetnie, jeśli to ujmę wszystko w jednym pliku. cu i uruchom go. Kiedy podzielę go na dwa oddzielne pliki, zaczynam otrzymywać błędy łączenia. Jak wszystkie moje ostatnie pytania, wiem, że to coś małego, ale co to jest?

KernelSupport.cu

#ifndef _KERNEL_SUPPORT_
#define _KERNEL_SUPPORT_

#include <iostream>
#include <MyKernel.cu>

int main( int argc, char** argv) 
{
    int* hostArray;
    int* deviceArray;
    const int arrayLength = 16;
    const unsigned int memSize = sizeof(int) * arrayLength;

    hostArray = (int*)malloc(memSize);
    cudaMalloc((void**) &deviceArray, memSize);

    std::cout << "Before device\n";
    for(int i=0;i<arrayLength;i++)
    {
        hostArray[i] = i+1;
        std::cout << hostArray[i] << "\n";
    }
    std::cout << "\n";

    cudaMemcpy(deviceArray, hostArray, memSize, cudaMemcpyHostToDevice);
    TestDevice <<< 4, 4 >>> (deviceArray);
    cudaMemcpy(hostArray, deviceArray, memSize, cudaMemcpyDeviceToHost);

    std::cout << "After device\n";
    for(int i=0;i<arrayLength;i++)
    {
        std::cout << hostArray[i] << "\n";
    }

    cudaFree(deviceArray);
    free(hostArray);

    std::cout << "Done\n";
}

#endif

MyKernel.cu

#ifndef _MY_KERNEL_
#define _MY_KERNEL_

__global__ void TestDevice(int *deviceArray)
{
    int idx = blockIdx.x*blockDim.x + threadIdx.x;
    deviceArray[idx] = deviceArray[idx]*deviceArray[idx];
}


#endif

Dziennik Budowy:

1>------ Build started: Project: CUDASandbox, Configuration: Debug x64 ------
1>Compiling with CUDA Build Rule...
1>"C:\CUDA\bin64\nvcc.exe"    -arch sm_10 -ccbin "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin"    -Xcompiler "/EHsc /W3 /nologo /O2 /Zi   /MT  "  -maxrregcount=32  --compile -o "x64\Debug\KernelSupport.cu.obj" "d:\Stuff\Programming\Visual Studio 2008\Projects\CUDASandbox\CUDASandbox\KernelSupport.cu" 
1>KernelSupport.cu
1>tmpxft_000016f4_00000000-3_KernelSupport.cudafe1.gpu
1>tmpxft_000016f4_00000000-8_KernelSupport.cudafe2.gpu
1>tmpxft_000016f4_00000000-3_KernelSupport.cudafe1.cpp
1>tmpxft_000016f4_00000000-12_KernelSupport.ii
1>Linking...
1>KernelSupport.cu.obj : error LNK2005: __device_stub__Z10TestDevicePi already defined in MyKernel.cu.obj
1>KernelSupport.cu.obj : error LNK2005: "void __cdecl TestDevice__entry(int *)" (?TestDevice__entry@@YAXPEAH@Z) already defined in MyKernel.cu.obj
1>D:\Stuff\Programming\Visual Studio 2008\Projects\CUDASandbox\x64\Debug\CUDASandbox.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Build log was saved at "file://d:\Stuff\Programming\Visual Studio 2008\Projects\CUDASandbox\CUDASandbox\x64\Debug\BuildLog.htm"
1>CUDASandbox - 3 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Uruchamiam Visual Studio 2008 na Windows 7 64bit.


Edit:

Myślę, że muszę to trochę rozwinąć. Efekt końcowy, którego tu Szukam, to posiadanie normalnego C++ aplikacja z czymś takim jak Main.cpp z eventem int main() i niech wszystko będzie działać stamtąd. W certains point w moim .kod cpp chcę być w stanie odwoływać się do bitów CUDA. Tak więc moje myślenie (i popraw mnie, jeśli istnieje bardziej standardowa konwencja tutaj) jest takie, że umieszczę kod jądra CUDA w ich plikach na. cu, a następnie mam plik. CU wspierający, który zajmie się rozmową z urządzeniem i wywołaniem funkcji jądra, a co nie.
Author: Mr Bell, 2010-01-19

4 answers

Umieszczasz mykernel.cu w kernelsupport.cu, gdy próbujesz połączyć kompilator widzi mykernel.cu dwa razy. Będziesz musiał utworzyć nagłówek definiujący TestDevice i dołączyć go zamiast tego.

Re komentarz:

Coś takiego powinno działać

// MyKernel.h
#ifndef mykernel_h
#define mykernel_h
__global__ void TestDevice(int* devicearray);
#endif

A następnie zmień plik zawierający na

//KernelSupport.cu
#ifndef _KERNEL_SUPPORT_
#define _KERNEL_SUPPORT_

#include <iostream>
#include <MyKernel.h>
// ...

Re your edit

Tak długo, jak nagłówek, którego używasz w kodzie c++, nie ma żadnych specyficznych rzeczy cuda (__kernel__,__global__, etc) powinno być dobrze łącząc c++ i Kod cuda.

 12
Author: Scott Wales,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-19 06:04:47

Jeśli spojrzysz na przykłady kodu CUDA SDK, mają one extern C definiuje, że funkcje referencyjne skompilowane z plików. cu. W ten sposób, pliki. cu są kompilowane przez nvcc i tylko połączone z głównym programem, podczas gdy .pliki cpp są kompilowane normalnie.

Na przykład w marchingCubes_kernel.cu ma ciało funkcyjne:

extern "C" void
launch_classifyVoxel( dim3 grid, dim3 threads, uint* voxelVerts, uint *voxelOccupied, uchar *volume,
                      uint3 gridSize, uint3 gridSizeShift, uint3 gridSizeMask, uint numVoxels,
                      float3 voxelSize, float isoValue)
{
    // calculate number of vertices need per voxel
    classifyVoxel<<<grid, threads>>>(voxelVerts, voxelOccupied, volume, 
                                     gridSize, gridSizeShift, gridSizeMask, 
                                     numVoxels, voxelSize, isoValue);
    cutilCheckMsg("classifyVoxel failed");
}
/ Align = "left" / cpp (gdzie znajduje się main ()) ma tylko definicję:
extern "C" void
launch_classifyVoxel( dim3 grid, dim3 threads, uint* voxelVerts, uint *voxelOccupied, uchar *volume,
                      uint3 gridSize, uint3 gridSizeShift, uint3 gridSizeMask, uint numVoxels,
                      float3 voxelSize, float isoValue);
Możesz umieścić je wplik h też.
 3
Author: tkerwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-19 06:24:05

Uzyskanie separacji jest w rzeczywistości dość proste, sprawdź tę odpowiedź , aby dowiedzieć się, jak ją skonfigurować. Następnie po prostu wprowadź kod hosta .pliki cpp i Kod urządzenia w plikach. cu, reguły budowania mówią Visual Studio, jak połączyć je ze sobą w finalnym pliku wykonywalnym.

Natychmiastowy problem w Twoim kodzie, że definiujesz funkcję {[0] } dwa razy, raz kiedy #include MyKernel.cu i raz, gdy skompilujesz MyKernel.cu niezależnie.

Będziesz potrzebował aby umieścić wrapper w pliku. cu - w tej chwili wywołujesz TestDevice<<<>>> z głównej funkcji, ale po przeniesieniu tego do .plik cpp zostanie skompilowany z cl.exe, który nie rozumie składni <<<>>>. Dlatego wystarczy zadzwonić TestDeviceWrapper(griddim, blockdim, params) w .plik cpp i dostarczyć tę funkcję w pliku. cu.

Jeśli chcesz przykład, próbka SobolQRNG w SDK osiąga ładne oddzielenie, chociaż nadal używa cutil i zawsze polecam unikanie cutil.

 3
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:08:59

Prostym rozwiązaniem jest wyłączenie budowy Twojego MyKernel.cu plik.

Właściwości - > ogólne - > wyłączone z budowy

Lepszym rozwiązaniem imo jest podzielenie jądra na plik CU i cuh i dołączenie tego, na przykład:

//kernel.cu
#include "kernel.cuh"
#include <cuda_runtime.h>

__global__ void increment_by_one_kernel(int* vals) {
  vals[threadIdx.x] += 1;
}

void increment_by_one(int* a) {
  int* a_d;

  cudaMalloc(&a_d, 1);
  cudaMemcpy(a_d, a, 1, cudaMemcpyHostToDevice);
  increment_by_one_kernel<<<1, 1>>>(a_d);
  cudaMemcpy(a, a_d, 1, cudaMemcpyDeviceToHost);

  cudaFree(a_d);
}

 

//kernel.cuh
#pragma once

void increment_by_one(int* a);

 

//main.cpp
#include "kernel.cuh"

int main() {
  int a[] = {1};

  increment_by_one(a);

  return 0;
}
 -3
Author: thebaldwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-20 00:56:48