使用多台 GPU 进行计算

▶ 源代码。使用不同的流来控制不同 GPU 上的运算任务。

 #include <stdio.h>
#include <timer.h>
#include <cuda_runtime.h>
#include "device_launch_parameters.h"
#include <helper_functions.h>
#include <helper_cuda.h>
#include "simpleMultiGPU.h" const int MAX_GPU_COUNT = ;
const int DATA_N = * ; __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 = ; for (int pos = tid; pos < N; pos += threadN)
sum += d_Input[pos]; d_Result[tid] = sum;
} int main(int argc, char **argv)
{
printf("\n\tStart.\n"); const int BLOCK_N = , THREAD_N = ;
const int ACCUM_N = BLOCK_N * THREAD_N;
int i, j, GPU_N;
float sumGPU;
TGPUplan plan[MAX_GPU_COUNT]; cudaGetDeviceCount(&GPU_N);
GPU_N = MIN(GPU_N, MAX_GPU_COUNT);
printf("\n\tDevice count: %i\n", GPU_N); // 准备计算数据
for (i = ; i < GPU_N; i++)
plan[i].dataN = DATA_N / GPU_N; // 计算数据量与设备数量没有对齐的部分
for (i = ; i < DATA_N % GPU_N; i++)
plan[i].dataN++; // 申请内存,初始化 h_data
for (i = ; i < GPU_N; i++)
{
cudaSetDevice(i);
cudaStreamCreate(&plan[i].stream);
cudaMalloc((void **)&plan[i].d_data, plan[i].dataN * sizeof(float));
cudaMalloc((void **)&plan[i].d_sum, ACCUM_N * sizeof(float));
cudaMallocHost((void **)&plan[i].h_sum_from_device, ACCUM_N * sizeof(float));
cudaMallocHost((void **)&plan[i].h_data, plan[i].dataN * sizeof(float)); for (j = ; j < plan[i].dataN; j++)
plan[i].h_data[j] = (float)rand() / (float)RAND_MAX;
} StartTimer();// 计时 // 调用各 GPU 进行计算,plan[i].d_data -> plan[i].d_sum -> plan[i].h_sum_from_device
for (i = ; i < GPU_N; i++)
{
cudaSetDevice(i);
cudaMemcpyAsync(plan[i].d_data, plan[i].h_data, plan[i].dataN * sizeof(float), cudaMemcpyHostToDevice, plan[i].stream);
reduceKernel<<<BLOCK_N, THREAD_N, , plan[i].stream>>>(plan[i].d_sum, plan[i].d_data, plan[i].dataN);
cudaMemcpyAsync(plan[i].h_sum_from_device, plan[i].d_sum, ACCUM_N *sizeof(float), cudaMemcpyDeviceToHost, plan[i].stream);
} // 处理 GPU 计算结果,plan[i].h_sum_from_device -> plan[i].h_sum -> sumGPU
for (i = ; i < GPU_N; i++)
{
cudaSetDevice(i);
cudaStreamSynchronize(plan[i].stream); for (j = , plan[i].h_sum = 0.0f; j < ACCUM_N; j++)
plan[i].h_sum += plan[i].h_sum_from_device[j];
}
for (i = , sumGPU = 0.0f; i < GPU_N; i++)// CPU 最后规约
sumGPU += plan[i].h_sum;
printf("\n\tGPU Processing time: %f (ms)\n", GetTimer()); // 使用 CPU 计算,plan[i].h_data -> sumCPU
double sumCPU = ;
for (i = ; i < GPU_N; i++)
{
for (j = ; j < plan[i].dataN; j++)
sumCPU += plan[i].h_data[j];
} // 检查结果
double diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);
printf("\n\tGPU sum: %f\n\tCPU sum: %f\n", sumGPU, sumCPU);
printf("\n\tRelative difference: %E, %s\n", diff, (diff < 1e-) ? "Passed" : "Failed"); //回收工作
for (i = ; i < GPU_N; i++)
{
cudaSetDevice(i);
cudaFreeHost(plan[i].h_data);
cudaFreeHost(plan[i].h_sum_from_device);
cudaFree(plan[i].d_sum);
cudaFree(plan[i].d_data);
cudaStreamDestroy(plan[i].stream);
} getchar();
return ;
}

▶ 输出结果

    Start.

    Device count: 

    GPU Processing time: 13.726471 (ms)

    GPU sum: 16779778.000000
CPU sum: 16779776.312309
Relative difference: 1.005789E-07, Passed

▶ 涨姿势

● 在使用不同的设备执行相关函数(包括 cudaFree 等主机函数)时要注意,使用函数 cudaSetDevice() 来切换设备。

0_Simple__MultiGPU的更多相关文章

随机推荐

  1. Appium笔记(二) 丶Appium的安装

    一.前言 Appium 中有个很重要的组件Appium-Server,它主要用来监听我们的移动设备(真机或模拟器),然将不同编程语言编写的 appium 测试脚本进行解析,然后,驱动移动设备来运行测试 ...

  2. CTF之猪圈密码

    猪圈密码又称济会密码,朱高密码,是一种简单的替代密码,所以安全性很低

  3. LG2731 骑马修栅栏 Riding the Fences

    题意 John是一个与其他农民一样懒的人.他讨厌骑马,因此从来不两次经过一个栅栏.你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次.John能从任何一个顶点( ...

  4. ACM常用算法

    数据结构 栈,队列,链表 哈希表,哈希数组 堆,优先队列 双端队列 可并堆 左偏堆 二叉查找树 Treap 伸展树 并查集 集合计数问题 二分图的识别 平衡二叉树 二叉排序树 线段树 一维线段树 二维 ...

  5. 在Spark上通过BulkLoad快速将海量数据导入到Hbase

    我们在<通过BulkLoad快速将海量数据导入到Hbase[Hadoop篇]>文中介绍了一种快速将海量数据导入Hbase的一种方法,而本文将介绍如何在Spark上使用Scala编写快速导入 ...

  6. android 学习过程中登陆失效的个人理解

    今天在学习的过程中,要做登陆失效的功能,所以就找了些资料.好好看了一下.研究了一番,慢慢的做出来了! 比方:你在一个手机端登陆了账号,在另外的一个手机端也登陆了账号,此时.前一个手机端的账号会提示登陆 ...

  7. 利用Xamaria构建Android应用-公交发车信息屏

    原文:利用Xamaria构建Android应用-公交发车信息屏 1.背景 在公交整个运营系统中,信息展示占据了很大一部分的内容.各种除了户外的各种LED拼接屏,还有用于室内信息提示用的LCD屏幕.对于 ...

  8. Qt下 QString转char*(转)

    Qt下面,字符串都用QString,确实给开发者提供了方便,想想VC里面定义的各种变量类型,而且函数参数类型五花八门,经常需要今年新那个类型转换 Qt再使用第三方开源库时,由于库的类型基本上都是标准的 ...

  9. qt creator 快捷键 (一)

    F1        查看帮助F2        跳转到函数定义(和Ctrl+鼠标左键一样的效果)Shift+F2    声明和定义之间切换F4        头文件和源文件之间切换Ctrl+1     ...

  10. 【Spring-AOP-学习笔记-6】@AfterThrowing增强处理简单示例

    项目结构 业务代码 @Component("hello") public class HelloImpl implements Hello {     // 定义一个简单方法,模拟 ...