CUDA之多设备并行

_梦魇花葬 2014-07-09 08:45:35
加精
在一台计算机中可以存在多个CUDA设备,通过CUDA API提供的上下文管理和设备管理功能可以使这些设备并行工作。采取这种方式建立的多设备系统可以提高单台机器的性能,节约空间和成本。
CUDA的设备管理功能与NVIDIA在图形应用中采用的SLI技术有很大的不同。SLI技术将多个GPU桥连起来,虚拟成一个设备,并由驱动层自动管理任务在各GPU间的分配,以及各个GPU间的通信;而CUDA的设备管理功能是由不同的线程管理各个GPU,每个GPU在一个时刻只能被一个线程使用。
CUDA runtime API通过设备管理功能对多个设备进行管理。由CUDA运行时API管理多设备,需要使用多个主机端线程。每个主机端线程在第一次调用其他CUDA运行时API函数之前,必须先由设备管理函数cuaSetDevic()与一个设备关联,并且以后也不能再次调用cuaSetDevic()函数与其他设备关联。主机端线程的数量可以多于设备数量,但一个时刻一个设备上只有一个主机端线程的上下文。为了达到最高性能,最好使用主机端线程数量与设备数量相同,每个线程与设备一一对应。
多个主机端线程会提高主机端CPU的开销,不过和多个GPU强大计算能力带来的性能提高相比显然这点投入是值得的。例如CUDA SDK中的simpleMultiGPU:
#include <stdio.h>
#include <assert.h>

// CUDA runtime
#include <cuda_runtime.h>

// helper functions and utilities to work with CUDA
#include <helper_functions.h>
#include <helper_cuda.h>
#include <timer.h>

#ifndef MAX
#define MAX(a,b) (a > b ? a : b)
#endif

#include "simpleMultiGPU.h"

////////////////////////////////////////////////////////////////////////////////
// Data configuration
////////////////////////////////////////////////////////////////////////////////
const int MAX_GPU_COUNT = 32;
const int DATA_N = 1048576 * 32;

////////////////////////////////////////////////////////////////////////////////
// Simple reduction kernel.
// Refer to the 'reduction' CUDA SDK sample describing
// reduction optimization strategies
////////////////////////////////////////////////////////////////////////////////
__global__ static void reduceKernel(float *d_Result, float *d_Input, int N)
{
const int tid = blockIdx.x * blockDim.x + threadIdx.x;
const int threadN = gridDim.x * blockDim.x;
float sum = 0;

for (int pos = tid; pos < N; pos += threadN)
sum += d_Input[pos];

d_Result[tid] = sum;
}

////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
//Solver config
TGPUplan plan[MAX_GPU_COUNT];

//GPU reduction results
float h_SumGPU[MAX_GPU_COUNT];

float sumGPU;
double sumCPU, diff;

int i, j, gpuBase, GPU_N;

const int BLOCK_N = 32;
const int THREAD_N = 256;
const int ACCUM_N = BLOCK_N * THREAD_N;

printf("Starting simpleMultiGPU\n");
checkCudaErrors(cudaGetDeviceCount(&GPU_N));

if (GPU_N > MAX_GPU_COUNT)
{
GPU_N = MAX_GPU_COUNT;
}

printf("CUDA-capable device count: %i\n", GPU_N);

printf("Generating input data...\n\n");

//Subdividing input data across GPUs
//Get data sizes for each GPU
for (i = 0; i < GPU_N; i++)
{
plan[i].dataN = DATA_N / GPU_N;
}

//Take into account "odd" data sizes
for (i = 0; i < DATA_N % GPU_N; i++)
{
plan[i].dataN++;
}

//Assign data ranges to GPUs
gpuBase = 0;

for (i = 0; i < GPU_N; i++)
{
plan[i].h_Sum = h_SumGPU + i;
gpuBase += plan[i].dataN;
}

//Create streams for issuing GPU command asynchronously and allocate memory (GPU and System page-locked)
for (i = 0; i < GPU_N; i++)
{
checkCudaErrors(cudaSetDevice(i));
checkCudaErrors(cudaStreamCreate(&plan[i].stream));
//Allocate memory
checkCudaErrors(cudaMalloc((void **)&plan[i].d_Data, plan[i].dataN * sizeof(float)));
checkCudaErrors(cudaMalloc((void **)&plan[i].d_Sum, ACCUM_N * sizeof(float)));
checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Sum_from_device, ACCUM_N * sizeof(float)));
checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Data, plan[i].dataN * sizeof(float)));

for (j = 0; j < plan[i].dataN; j++)
{
plan[i].h_Data[j] = (float)rand() / (float)RAND_MAX;
}
}

//Start timing and compute on GPU(s)
printf("Computing with %d GPUs...\n", GPU_N);
StartTimer();

//Copy data to GPU, launch the kernel and copy data back. All asynchronously
for (i = 0; i < GPU_N; i++)
{
//Set device
checkCudaErrors(cudaSetDevice(i));

//Copy input data from CPU
checkCudaErrors(cudaMemcpyAsync(plan[i].d_Data, plan[i].h_Data, plan[i].dataN * sizeof(float), cudaMemcpyHostToDevice, plan[i].stream));

//Perform GPU computations
reduceKernel<<<BLOCK_N, THREAD_N, 0, plan[i].stream>>>(plan[i].d_Sum, plan[i].d_Data, plan[i].dataN);
getLastCudaError("reduceKernel() execution failed.\n");

//Read back GPU results
checkCudaErrors(cudaMemcpyAsync(plan[i].h_Sum_from_device, plan[i].d_Sum, ACCUM_N *sizeof(float), cudaMemcpyDeviceToHost, plan[i].stream));
}

//Process GPU results
for (i = 0; i < GPU_N; i++)
{
float sum;

//Set device
checkCudaErrors(cudaSetDevice(i));

//Wait for all operations to finish
cudaStreamSynchronize(plan[i].stream);

//Finalize GPU reduction for current subvector
sum = 0;

for (j = 0; j < ACCUM_N; j++)
{
sum += plan[i].h_Sum_from_device[j];
}

*(plan[i].h_Sum) = (float)sum;

//Shut down this GPU
checkCudaErrors(cudaFreeHost(plan[i].h_Sum_from_device));
checkCudaErrors(cudaFree(plan[i].d_Sum));
checkCudaErrors(cudaFree(plan[i].d_Data));
checkCudaErrors(cudaStreamDestroy(plan[i].stream));
}

sumGPU = 0;

for (i = 0; i < GPU_N; i++)
{
sumGPU += h_SumGPU[i];
}

printf(" GPU Processing time: %f (ms)\n\n", GetTimer());

// Compute on Host CPU
printf("Computing with Host CPU...\n\n");

sumCPU = 0;

for (i = 0; i < GPU_N; i++)
{
for (j = 0; j < plan[i].dataN; j++)
{
sumCPU += plan[i].h_Data[j];
}
}

// Compare GPU and CPU results
printf("Comparing GPU and Host CPU results...\n");
diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);
printf(" GPU sum: %f\n CPU sum: %f\n", sumGPU, sumCPU);
printf(" Relative difference: %E \n\n", diff);

// Cleanup and shutdown
for (i = 0; i < GPU_N; i++)
{
checkCudaErrors(cudaSetDevice(i));
checkCudaErrors(cudaFreeHost(plan[i].h_Data));
cudaDeviceReset();
}

exit((diff < 1e-5) ? EXIT_SUCCESS : EXIT_FAILURE);
}

NVIDIA最近发布的CUDA6.0里面有一项新的改动:
多GPU扩展(Multi-GPU Scaling)
重新设计的BLAS、FFT GPU库,单个节点可自动支持最多八颗GPU,双精度浮点性能可超过9TFlops,并且支持最多512GB 的更大负载。
这里的多GPU扩展和上述的多设备并行是不一样的,而且CUDA6.0中的多GPU扩展是有一定的限制的,并不能在所有的NVIDIA硬件上实施。我也在研究这个,希望大家可以多讨论下!
...全文
2489 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
_梦魇花葬 2014-07-17
  • 打赏
  • 举报
回复
明天,我准备发一篇关于CUDA函数高亮的东西,因为有些小伙伴没有弄好!请大家多多支持!
_梦魇花葬 2014-07-16
  • 打赏
  • 举报
回复
是的,这两个是不一样的,上面有说明的呀!~~
yangfeilong1988 2014-07-15
  • 打赏
  • 举报
回复
也就是说两个是不同的概念,不同的东西吗?
_梦魇花葬 2014-07-15
  • 打赏
  • 举报
回复
这个是多设备并行,和多GPU并行是不一样的!可以看看书本!
yangfeilong1988 2014-07-15
  • 打赏
  • 举报
回复
版主,你这个东西和多个GPU并行是一样的吗?感觉有点不是很懂呀!~~
_梦魇花葬 2014-07-15
  • 打赏
  • 举报
回复
但是,单个GPU的话,能处理的数据量还是少的,这时候 需要多个GPU来共同处理,可以提高处理速度和处理量,但这和多设备并行处理是不一样的。各位小伙伴,要考虑到这点哟!
_梦魇花葬 2014-07-15
  • 打赏
  • 举报
回复
目前现实生活中,需要处理的数据越来越多了,如何能更好的,快速的去处理数据,是一个很重要的问题。GPU在处理数据上有很明显的速度优势!
伊顺鸣 2014-07-14
  • 打赏
  • 举报
回复
什么样的啊。。
dior1111 2014-07-14
  • 打赏
  • 举报
回复
_梦魇花葬 2014-07-14
  • 打赏
  • 举报
回复
恩!这个是多个设备并行呀!可以提高计算速度!
xusir98 2014-07-13
  • 打赏
  • 举报
回复
sang_4587_109 2014-07-10
  • 打赏
  • 举报
回复
顶!比较强 学习一下
koko200147 2014-07-10
  • 打赏
  • 举报
回复
高人,玩底层的啊,
liuhai200913 2014-07-10
  • 打赏
  • 举报
回复
《GPU高性能计算之CUDA》实例。 GPU高性能计算系列丛书的第一本《GPU高性能计算之CUDA》已经出版,由张舒,褚艳利,赵开勇,张钰勃所编写。本书除了详细介绍了CUDA的软硬件架构以及C for CUDA程序开发和优化的策略外,还包含有大量的实例供读者学习参考用。 下表是各个实例的介绍列表。 文件夹 对应书中章节 备注 ACsearch_DPPcompact_with_driver 5.2.2 AC多模式匹配算法 asyncAPI 2.5 异步API调用示例 bandwidthTest 2.3.6 带宽测试 Bitonic 5.1.1 双调排序网络 conjugateGradient 5.2.1 共轭梯度算法,CUBLAS实现 cudaMPI 2.7.3 CUDA+MPI管理GPU集群 cudaOpenMP 2.7.2 CUDA+OpenMP管理多GPU deviceQuery 2.1.4 设备查询 histKernel 2.4.3 亮度直方图统计 matrixAssign 2.1.4 矩阵赋值 matrixMul 4.7.1 矩阵乘法,利用shared memory matrixMul_Berkeley 4.7.1 矩阵乘法,利用register reduction 4.7.2 并行归约(缩减)程序 scan 5.1.2 Scan算法,例如计算前缀和 scanLargeArray 5.1.2 Scan算法,可以处理大数组 simpleCUBLAS 5.1.3 CUBLAS库的简单应用 simpleCUFFT 5.1.4 CUFFT库的简单应用 simpleD3D9 2.6.2 CUDA与Direct3D 9互操作 simpleD3D10 2.6.2 CUDA与Direct3D10互操作 simpleGL 2.6.1 CUDA与OpenGL互操作 simpleMultiGPU 2.7.1 多设备控制 simpleStreams 2.5.2 流的使用演示 simpleTexture 2.3.8 简单的纹理使用 simpleTextureDrv 2.3.8 简单的纹理使用,驱动API 实现 sortingNetworks 5.1.1 双调排序网络,处理大数组 threadMigration 2.7.1 通过上下文管理和设备管理功能实现多设备并行计算 timing 4.2.1 设备端测时 transpose 4.7.3 矩阵转置 transposeDiagonal 4.7.3 矩阵转置,考虑partition conflict VectorAdd 2.2.3/2.3.4 矢量加 VectorAddDrv 2.2.3/2.3.4 矢量加,驱动API实现 【备注】以上工程,均在Windows XP 64-bit + Tesla C1060 + CUDA 2.3 + VS2005环境下测试通过。

357

社区成员

发帖
与我相关
我的任务
社区描述
CUDA高性能计算讨论
社区管理员
  • CUDA高性能计算讨论社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧