1. 程式人生 > >VS2010 CUDA和C++混合編譯

VS2010 CUDA和C++混合編譯

在做專案整合的時候需要用到cpp和cuda檔案聯調,自己摸索了兩種方式實現cpp和cu檔案混合編譯。

本文環境:

  • windows7 64位
  • VS2010
  • CUDA5.5
  • 英偉達顯示卡Tesla C1060

前言

裝好CUDA 5.5 sdk後,預設會自動新增好系統環境變數。
因此不需要額外配置,不過為了保險起見,可以選擇性地新增以下環境變數:
CUDA_BIN_PATH  %CUDA_PATH%\bin
CUDA_LIB_PATH  %CUDA_PATH%\lib\Win32
CUDA_SDK_BIN  %CUDA_SDK_PATH%\bin\Win32
CUDA_SDK_LIB  %CUDA_SDK_PATH%\common\lib\Win32
CUDA_SDK_PATH  C:\cuda\cudasdk\common
這時可以開啟CUDA自帶的sample執行一下,執行能通過才可以繼續下面的內容————cpp和cuda聯調。

方法一:先建立cuda工程,再新增cpp檔案

1.開啟vs2010,新建一個cuda專案,名稱CudaCpp。


2.cuda預設建立的工程是如下,實現了兩個一維向量的並行相加。kernel函式和執行函式還有main函式全都寫在了一個cu檔案裡。


3.接下來在工程裡新增一個空的cpp檔案。將原來cu檔案裡main函式裡的內容剪下到cpp檔案main函式裡。

為了讓cpp能夠呼叫cu檔案裡面的函式,在addWithCuda函式前加上extern "C"關鍵字  (注意C大寫,為什麼addKernel不用加呢?因為cpp裡面直接呼叫的是addWithCuda)


4.在cpp裡也要加上addWithCuda函式的完整前向宣告。下圖就是工程的完整結構


5.可以在cpp裡的main函式return之間加入getchar()防止執行後一閃就退出,加上system("pause")或者直接ctrl+F5也行。

執行結果:


下面貼出CudaCpp專案程式碼。

kernel.cu

[plain] view plain copy print?
  1. #include "cuda_runtime.h"  
  2. #include "device_launch_parameters.h"  
  3. #include <stdio.h>  
  4. __global__ void addKernel(int *c, const int *a, const int *b)  
  5. {  
  6.     int i = threadIdx.x;  
  7.     c[i] = a[i] + b[i];  
  8. }  
  9. // Helper function for using CUDA to add vectors in parallel.  
  10. extern "C"  
  11. cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)  
  12. {  
  13.     int *dev_a = 0;  
  14.     int *dev_b = 0;  
  15.     int *dev_c = 0;  
  16.     cudaError_t cudaStatus;  
  17.     // Choose which GPU to run on, change this on a multi-GPU system.  
  18.     cudaStatus = cudaSetDevice(0);  
  19.     if (cudaStatus != cudaSuccess) {  
  20.         fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");  
  21.         goto Error;  
  22.     }  
  23.     // Allocate GPU buffers for three vectors (two input, one output)    .  
  24.     cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));  
  25.     if (cudaStatus != cudaSuccess) {  
  26.         fprintf(stderr, "cudaMalloc failed!");  
  27.         goto Error;  
  28.     }  
  29.     cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));  
  30.     if (cudaStatus != cudaSuccess) {  
  31.         fprintf(stderr, "cudaMalloc failed!");  
  32.         goto Error;  
  33.     }  
  34.     cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));  
  35.     if (cudaStatus != cudaSuccess) {  
  36.         fprintf(stderr, "cudaMalloc failed!");  
  37.         goto Error;  
  38.     }  
  39.     // Copy input vectors from host memory to GPU buffers.  
  40.     cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);  
  41.     if (cudaStatus != cudaSuccess) {  
  42.         fprintf(stderr, "cudaMemcpy failed!");  
  43.         goto Error;  
  44.     }  
  45.     cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);  
  46.     if (cudaStatus != cudaSuccess) {  
  47.         fprintf(stderr, "cudaMemcpy failed!");  
  48.         goto Error;  
  49.     }  
  50.     // Launch a kernel on the GPU with one thread for each element.  
  51.     addKernel<<<1, size>>>(dev_c, dev_a, dev_b);  
  52.     // Check for any errors launching the kernel  
  53.     cudaStatus = cudaGetLastError();  
  54.     if (cudaStatus != cudaSuccess) {  
  55.         fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));  
  56.         goto Error;  
  57.     }  
  58.     // cudaDeviceSynchronize waits for the kernel to finish, and returns  
  59.     // any errors encountered during the launch.  
  60.     cudaStatus = cudaDeviceSynchronize();  
  61.     if (cudaStatus != cudaSuccess) {  
  62.         fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);  
  63.         goto Error;  
  64.     }  
  65.     // Copy output vector from GPU buffer to host memory.  
  66.     cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);  
  67.     if (cudaStatus != cudaSuccess) {  
  68.         fprintf(stderr, "cudaMemcpy failed!");  
  69.         goto Error;  
  70.     }  
  71. Error:  
  72.     cudaFree(dev_c);  
  73.     cudaFree(dev_a);  
  74.     cudaFree(dev_b);  
  75.     return cudaStatus;  
  76. }  
save_snippets.png
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

__global__ void addKernel(int *c, const int *a, const int *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}
// Helper function for using CUDA to add vectors in parallel.
extern "C"
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
    int *dev_a = 0;
    int *dev_b = 0;
    int *dev_c = 0;
    cudaError_t cudaStatus;

    // Choose which GPU to run on, change this on a multi-GPU system.
    cudaStatus = cudaSetDevice(0);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

    // Allocate GPU buffers for three vectors (two input, one output)    .
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    // Copy input vectors from host memory to GPU buffers.
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    // Launch a kernel on the GPU with one thread for each element.
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);

    // Check for any errors launching the kernel
    cudaStatus = cudaGetLastError();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
        goto Error;
    }
    
    // cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch.
    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

    // Copy output vector from GPU buffer to host memory.
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

Error:
    cudaFree(dev_c);
    cudaFree(dev_a);
    cudaFree(dev_b);
    
    return cudaStatus;
}
main.cpp [cpp] view plain copy print?
  1. #include <stdio.h>
  2. #include "cuda_runtime.h"
  3. #include "device_launch_parameters.h"
  4. extern"C"
  5.     cudaError_t addWithCuda(int *c, constint *a, constint *b, unsigned int size);  
  6. int main()  
  7. {  
  8.     constint arraySize = 5;  
  9.     constint a[arraySize] = { 1, 2, 3, 4, 5 };  
  10.     constint b[arraySize] = { 10, 20, 30, 40, 50 };  
  11.     int c[arraySize] = { 0 };  
  12.     // Add vectors in parallel.
  13.     cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);  
  14.     if (cudaStatus != cudaSuccess) {  
  15.         fprintf(stderr, "addWithCuda failed!");  
  16.         return 1;  
  17.     }  
  18.     printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",  
  19.         c[0], c[1], c[2], c[3], c[4]);  
  20.     printf("cuda工程中呼叫cpp成功!\n");  
  21.     // cudaDeviceReset must be called before exiting in order for profiling and
  22.     // tracing tools such as Nsight and Visual Profiler to show complete traces.
  23.     cudaStatus = cudaDeviceReset();  
  24.     if (cudaStatus != cudaSuccess) {  
  25.         fprintf(stderr, "cudaDeviceReset failed!");  
  26.         return 1;  
  27.     }  
  28.     getchar(); //here we want the console to hold for a while
  29.     return 0;  
  30. }  
save_snippets.png
#include <stdio.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

extern "C"
	cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
int main()
{
	const int arraySize = 5;
	const int a[arraySize] = { 1, 2, 3, 4, 5 };
	const int b[arraySize] = { 10, 20, 30, 40, 50 };
	int c[arraySize] = { 0 };

	// Add vectors in parallel.
	cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "addWithCuda failed!");
		return 1;
	}

	printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
		c[0], c[1], c[2], c[3], c[4]);
	printf("cuda工程中呼叫cpp成功!\n");

	// cudaDeviceReset must be called before exiting in order for profiling and
	// tracing tools such as Nsight and Visual Profiler to show complete traces.
	cudaStatus = cudaDeviceReset();
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "cudaDeviceReset failed!");
		return 1;
	}
	getchar(); //here we want the console to hold for a while
	return 0;
}

方法二:先建立cpp工程,再新增cu檔案

方法一由於是cuda工程是自動建立的,所以比較簡單,不需要多少額外的配置。而在cpp工程裡面新增cu就要複雜一些。為了簡單起見,這裡採用console程式講解,至於MFC或者Direct3D程式同理。

1.建立一個空的win32控制檯工程,名稱CppCuda。

2.然後右鍵工程-->新增一個cu檔案



3.將方法一中cu和cpp檔案的程式碼分別拷貝到這個工程裡來(做了少許修改,extern "C"關鍵字和某些標頭檔案不要忘了加),工程結構如圖:


這個時候編譯是通不過的,需要作一些配置。

4.關鍵的一步,右鍵工程-->生成自定義 ,將對話方塊中CUDA5.5前面的勾打上。


這時點選 工程-->屬性,會發現多了CUDA連結器這一項。


5.關鍵的一步,右鍵kernel.cu檔案-->屬性,在 常規-->項型別 裡面選擇CUDA C/C++(由於cu檔案是由nvcc編譯的,這裡要修改編譯連結屬性)


6.工程-->屬性-->連結器-->附加依賴項,加入cudart.lib


7.工具-->選項-->文字編輯器-->副檔名 新增cu \cuh兩個副檔名


8.至此配置成功。執行一下:


9.為了更加確信cuda中的函式確實被呼叫,在main.cpp裡面呼叫cuda函式的地方加入了一個斷點。


單步執行一下。


可以看到程式跳到了cu檔案裡去執行了,說明cpp呼叫cuda函式成功。


貼上程式碼(其實跟方式一基本一樣,沒怎麼改),工程CppCuda

kernel.cu

[plain] view plain copy print?
  1. #include "cuda_runtime.h"  
  2. #include "device_launch_parameters.h"  
  3. #include <stdio.h>  
  4. //cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);  
  5. __global__ void addKernel(int *c, const int *a, const int *b)  
  6. {  
  7.     int i = threadIdx.x;  
  8.     c[i] = a[i] + b[i];  
  9. }  
  10. // Helper function for using CUDA to add vectors in parallel.  
  11. extern "C"  
  12. cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)  
  13. {  
  14.     int *dev_a = 0;  
  15.     int *dev_b = 0;  
  16.     int *dev_c = 0;  
  17.     cudaError_t cudaStatus;  
  18.     // Choose which GPU to run on, change this on a multi-GPU system.  
  19.     cudaStatus = cudaSetDevice(0);  
  20.     if (cudaStatus != cudaSuccess) {  
  21.         fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");  
  22.         goto Error;  
  23.     }  
  24.     // Allocate GPU buffers for three vectors (two input, one output)    .  
  25.     cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));  
  26.     if (cudaStatus != cudaSuccess) {  
  27.         fprintf(stderr, "cudaMalloc failed!");  
  28.         goto Error;  
  29.     }  
  30.     cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));  
  31.     if (cudaStatus != cudaSuccess) {  
  32.         fprintf(stderr, "cudaMalloc failed!");  
  33.         goto Error;  
  34.     }  
  35.     cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));  
  36.     if (cudaStatus != cudaSuccess) {  
  37.         fprintf(stderr, "cudaMalloc failed!");  
  38.         goto Error;  
  39.     }  
  40.     // Copy input vectors from host memory to GPU buffers.  
  41.     cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);  
  42.     if (cudaStatus != cudaSuccess) {  
  43.         fprintf(stderr, "cudaMemcpy failed!");  
  44.         goto Error;  
  45.     }  
  46.     cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);  
  47.     if (cudaStatus != cudaSuccess) {  
  48.         fprintf(stderr, "cudaMemcpy failed!");  
  49.         goto Error;  
  50.     }  
  51.     // Launch a kernel on the GPU with one thread for each element.  
  52.     addKernel<<<1, size>>>(dev_c, dev_a, dev_b);  
  53.     // Check for any errors launching the kernel  
  54.     cudaStatus = cudaGetLastError();  
  55.     if (cudaStatus != cudaSuccess) {  
  56.         fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));  
  57.         goto Error;  
  58.     }  
  59.     // cudaDeviceSynchronize waits for the kernel to finish, and returns  
  60.     // any errors encountered during the launch.  
  61.     cudaStatus = cudaDeviceSynchronize();  
  62.     if (cudaStatus != cudaSuccess) {  
  63.         fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);  
  64.         goto Error;  
  65.     }  
  66.     // Copy output vector from GPU buffer to host memory.  
  67.     cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);  
  68.     if (cudaStatus != cudaSuccess) {  
  69.         fprintf(stderr, "cudaMemcpy failed!");  
  70.         goto Error;  
  71.     }  
  72. Error:  
  73.     cudaFree(dev_c);  
  74.     cudaFree(dev_a);  
  75.     cudaFree(dev_b);  
  76.     return cudaStatus;  
  77. }  
save_snippets.png
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

//cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
__global__ void addKernel(int *c, const int *a, const int *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}
// Helper function for using CUDA to add vectors in parallel.
extern "C"
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
    int *dev_a = 0;
    int *dev_b = 0;
    int *dev_c = 0;
    cudaError_t cudaStatus;

    // Choose which GPU to run on, change this on a multi-GPU system.
    cudaStatus = cudaSetDevice(0);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

    // Allocate GPU buffers for three vectors (two input, one output)    .
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    // Copy input vectors from host memory to GPU buffers.
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    // Launch a kernel on the GPU with one thread for each element.
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);

    // Check for any errors launching the kernel
    cudaStatus = cudaGetLastError();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
        goto Error;
    }
    
    // cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch.
    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

    // Copy output vector from GPU buffer to host memory.
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

Error:
    cudaFree(dev_c);
    cudaFree(dev_a);
    cudaFree(dev_b);
    
    return cudaStatus;
}
main.cpp [cpp] view plain copy print?
  1. #include <iostream>
  2. #include "cuda_runtime.h"
  3. #include "device_launch_parameters.h"
  4. usingnamespace std;  
  5. extern"C"
  6.     cudaError_t addWithCuda(int *c, constint *a, constint *b, unsigned int size);  
  7. int main(int argc,char **argv)  
  8. {  
  9.     constint arraySize = 5;  
  10.     constint a[arraySize] = { 1, 2, 3, 4, 5 };  
  11.     constint b[arraySize] = { 10, 20, 30, 40, 50 };  
  12.     int c[arraySize] = { 0 };  
  13.     // Add vectors in parallel.
  14.     cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);  
  15.     if (cudaStatus != cudaSuccess) {  
  16.         fprintf(stderr, "addWithCuda failed!");  
  17.         return 1;  
  18.     }  
  19.     cout<<"{1,2,3,4,5} + {10,20,30,40,50} = {"<<c[0]<<','<<c[1]<<','<<c[2]<<','<<c[3]<<'}'<<endl;  
  20.     printf("cpp工程中呼叫cu成功!\n");  
  21.     // cudaDeviceReset must be called before exiting in order for profiling and
  22.     // tracing tools such as Nsight and Visual Profiler to show complete traces.
  23.     cudaStatus = cudaDeviceReset();  
  24.     if (cudaStatus != cudaSuccess) {  
  25.         fprintf(stderr, "cudaDeviceReset failed!");  
  26.         return 1;  
  27.     }  
  28.     system("pause"); //here we want the console to hold for a while
  29.     return 0;  
  30. }  
save_snippets.png
#include <iostream>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
using namespace std;

extern "C"
	cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
int main(int argc,char **argv)
{
	const int arraySize = 5;
	const int a[arraySize] = { 1, 2, 3, 4, 5 };
	const int b[arraySize] = { 10, 20, 30, 40, 50 };
	int c[arraySize] = { 0 };

	// Add vectors in parallel.
	cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "addWithCuda failed!");
		return 1;
	}

	cout<<"{1,2,3,4,5} + {10,20,30,40,50} = {"<<c[0]<<','<<c[1]<<','<<c[2]<<','<<c[3]<<'}'<<endl;
	printf("cpp工程中呼叫cu成功!\n");

	// cudaDeviceReset must be called before exiting in order for profiling and
	// tracing tools such as Nsight and Visual Profiler to show complete traces.
	cudaStatus = cudaDeviceReset();
	if (cudaStatus != cudaSuccess) {
		fprintf(stderr, "cudaDeviceReset failed!");
		return 1;
	}
	system("pause"); //here we want the console to hold for a while
	return 0;
}
注意有時候編譯出問題,把  "device_launch_parameters.h"這個標頭檔案去掉就好了(去掉之後就不能調裡面的函式或變量了),至於為什麼,還不是很清楚。

以後將cu檔案加入到任何MFC,qt,D3D或者OpenGL等C++工程中步驟都是類似的。