CUDA Samples:Vector Add
生活随笔
收集整理的這篇文章主要介紹了
CUDA Samples:Vector Add
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
以下CUDA sample是分別用C++和CUDA實現的兩向量相加操作,參考CUDA 8.0中的sample:C:\ProgramData\NVIDIA Corporation\CUDA Samples\v8.0\0_Simple,并對其中使用到的CUDA函數進行了解說,各個文件內容如下:
common.hpp:
#ifndef FBC_CUDA_TEST_COMMON_HPP_
#define FBC_CUDA_TEST_COMMON_HPP_#define PRINT_ERROR_INFO(info) { \fprintf(stderr, "Error: %s, file: %s, func: %s, line: %d\n", #info, __FILE__, __FUNCTION__, __LINE__); \return -1; }#define EXP 1.0e-5#endif // FBC_CUDA_TEST_COMMON_HPP_
simple.hpp:
#ifndef FBC_CUDA_TEST_SIMPLE_HPP_
#define FBC_CUDA_TEST_SIMPLE_HPP_// reference: C:\ProgramData\NVIDIA Corporation\CUDA Samples\v8.0\0_Simple
int test_vectorAdd();
int vectorAdd_cpu(const float *A, const float *B, float *C, int numElements);
int vectorAdd_gpu(const float *A, const float *B, float *C, int numElements);#endif // FBC_CUDA_TEST_SIMPLE_HPP_
simple.cpp:
#include "simple.hpp"
#include <random>
#include <iostream>
#include <vector>
#include "common.hpp"// =========================== vector add =============================
int test_vectorAdd()
{// Vector addition: C = A + B, implements element by element vector additionconst int numElements{ 50000 };std::vector<float> A(numElements), B(numElements), C1(numElements), C2(numElements);// Initialize vectorfor (int i = 0; i < numElements; ++i) {A[i] = rand() / (float)RAND_MAX;B[i] = rand() / (float)RAND_MAX;}int ret = vectorAdd_cpu(A.data(), B.data(), C1.data(), numElements);if (ret != 0) PRINT_ERROR_INFO(vectorAdd_cpu);ret = vectorAdd_gpu(A.data(), B.data(), C2.data(), numElements);if (ret != 0) PRINT_ERROR_INFO(vectorAdd_gpu);for (int i = 0; i < numElements; ++i) {if (fabs(C1[i] - C2[i]) > EXP) {fprintf(stderr, "Result verification failed at element %d!\n", i);return -1;}}return 0;
}int vectorAdd_cpu(const float* A, const float* B, float* C, int numElements)
{for (int i = 0; i < numElements; ++i) {C[i] = A[i] + B[i];}return 0;
}
simple.cu:
#include "simple.hpp"
#include <iostream>
#include <cuda_runtime.h> // For the CUDA runtime routines (prefixed with "cuda_")
#include <device_launch_parameters.h>
#include "common.hpp"// reference: C:\ProgramData\NVIDIA Corporation\CUDA Samples\v8.0\0_Simple// =========================== vector add =============================
/* __global__: 函數類型限定符;在設備上運行;在主機端調用,計算能力3.2及以上可以在
設備端調用;聲明的函數的返回值必須是void類型;對此類型函數的調用是異步的,即在
設備完全完成它的運行之前就返回了;對此類型函數的調用必須指定執行配置,即用于在
設備上執行函數時的grid和block的維度,以及相關的流(即插入<<< >>>運算符);
a kernel,表示此函數為內核函數(運行在GPU上的CUDA并行計算函數稱為kernel(內核函
數),內核函數必須通過__global__函數類型限定符定義);*/
__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)
{/* blockDim: 內置變量,用于說明每個block的維度與尺寸.為dim3類型,包含了block在三個維度上的尺寸信息;對于所有線程塊來說,這個變量是一個常數,保存的是線程塊中每一維的線程數量;blockIdx: 內置變量,變量中包含的值就是當前執行設備代碼的線程塊的索引;用于說明當前thread所在的block在整個grid中的位置,blockIdx.x取值范圍是[0,gridDim.x-1],blockIdx.y取值范圍是[0, gridDim.y-1].為uint3類型,包含了一個block在grid中各個維度上的索引信息;threadIdx: 內置變量,變量中包含的值就是當前執行設備代碼的線程索引;用于說明當前thread在block中的位置;如果線程是一維的可獲取threadIdx.x,如果是二維的還可獲取threadIdx.y,如果是三維的還可獲取threadIdx.z;為uint3類型,包含了一個thread在block中各個維度的索引信息 */int i = blockDim.x * blockIdx.x + threadIdx.x;if (i < numElements) {C[i] = A[i] + B[i];}
}int vectorAdd_gpu(const float *A, const float *B, float *C, int numElements)
{/* Error code to check return values for CUDA callscudaError_t: CUDA Error types, 枚舉類型,CUDA錯誤碼,成功返回cudaSuccess(0),否則返回其它(>0) */cudaError_t err{ cudaSuccess };size_t length{ numElements * sizeof(float) };float *d_A{ nullptr }, *d_B{ nullptr }, *d_C{ nullptr };// cudaMalloc: 在設備端分配內存err = cudaMalloc(&d_A, length);if (err != cudaSuccess) {// cudaGetErrorString: 返回錯誤碼的描述字符串fprintf(stderr, "Failed to allocate device vector A (error code %s)!\n",cudaGetErrorString(err));return -1;}err = cudaMalloc(&d_B, length);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaMalloc);err = cudaMalloc(&d_C, length);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaMalloc);/* cudaMemcpy: 在主機端和設備端拷貝數據,此函數第四個參數僅能是下面之一:(1). cudaMemcpyHostToHost: 拷貝數據從主機端到主機端(2). cudaMemcpyHostToDevice: 拷貝數據從主機端到設備端(3). cudaMemcpyDeviceToHost: 拷貝數據從設備端到主機端(4). cudaMemcpyDeviceToDevice: 拷貝數據從設備端到設備端(5). cudaMemcpyDefault: 從指針值自動推斷拷貝數據方向,需要支持統一虛擬尋址(CUDA6.0及以上版本) */err = cudaMemcpy(d_A, A, length, cudaMemcpyHostToDevice);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaMemcpy);err = cudaMemcpy(d_B, B, length, cudaMemcpyHostToDevice);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaMemcpy);// Launch the Vector Add CUDA kernelconst int threadsPerBlock{ 256 };const int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock;fprintf(stderr, "CUDA kernel launch with %d blocks of %d threads\n",blocksPerGrid, threadsPerBlock);/* <<< >>>: 為CUDA引入的運算符,指定線程網格和線程塊維度等,傳遞執行參數給CUDA編譯器和運行時系統,用于說明內核函數中的線程數量,以及線程是如何組織的;尖括號中這些參數并不是傳遞給設備代碼的參數,而是告訴運行時如何啟動設備代碼,傳遞給設備代碼本身的參數是放在圓括號中傳遞的,就像標準的函數調用一樣;不同計算能力的設備對線程的總數和組織方式有不同的約束;必須先為kernel中用到的數組或變量分配好足夠的空間,再調用kernel函數,否則在GPU計算時會發生錯誤,例如越界等;使用運行時API時,需要在調用的內核函數名與參數列表直接以<<<Dg,Db,Ns,S>>>的形式設置執行配置,其中:Dg是一個dim3型變量,用于設置grid的維度和各個維度上的尺寸.設置好Dg后,grid中將有Dg.x*Dg.y個block,Dg.z必須為1;Db是一個dim3型變量,用于設置block的維度和各個維度上的尺寸.設置好Db后,每個block中將有Db.x*Db.y*Db.z個thread;Ns是一個size_t型變量,指定各塊為此調用動態分配的共享存儲器大小,這些動態分配的存儲器可供聲明為外部數組(extern __shared__)的其他任何變量使用;Ns是一個可選參數,默認值為0;S為cudaStream_t類型,用于設置與內核函數關聯的流.S是一個可選參數,默認值0. */vectorAdd <<<blocksPerGrid, threadsPerBlock >>>(d_A, d_B, d_C, numElements);/* cudaGetLastError: 在同一個主機線程中,返回運行時調用中產生的最后一個錯誤并將其重置為cudaSuccess;此函數也可能返回以前異步啟動的錯誤碼;當有多個錯誤在對cudaGetLastError的調用之間發生時,僅最后一個錯誤會被報告;kernel的啟動是異步的,為了定位它是否出錯,一般需要加上cudaDeviceSynchronize函數進行同步,然后再調用cudaGetLastError函數;*/err = cudaGetLastError();if (err != cudaSuccess) PRINT_ERROR_INFO(cudaGetLastError);// Copy the device result vector in device memory to the host result vector in host memory.err = cudaMemcpy(C, d_C, length, cudaMemcpyDeviceToHost);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaMemcpy);// cudaFree: 釋放設備上由cudaMalloc函數分配的內存err = cudaFree(d_A);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaFree);err = cudaFree(d_B);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaFree);err = cudaFree(d_C);if (err != cudaSuccess) PRINT_ERROR_INFO(cudaFree);return err;
}
執行結果如下:
GitHub:? https://github.com/fengbingchun/CUDA_Test
總結
以上是生活随笔為你收集整理的CUDA Samples:Vector Add的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 激活函数之ReLU/softplus介绍
- 下一篇: C++中const指针用法汇总