0_Simple__simpleP2P

时间:2022-09-28 11:27:47

使用 P2P 特性在 GPU 之间传输、读写数据。

▶ 源代码。包括 P2P 使用前的各项检查,设备之间的数据互拷,主机和设备之间数据传输和相互访问。

 #include <stdlib.h>
#include <stdio.h>
#include <cuda_runtime.h>
#include "device_launch_parameters.h"
#include <helper_cuda.h>
#include <helper_functions.h> #define MAX_GPU_COUNT 64 __global__ void SimpleKernel(float *src, float *dst)
{
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
dst[idx] = src[idx] * 2.0f;
} inline bool IsGPUCapableP2P(cudaDeviceProp *pProp)
{
#ifdef _WIN32
return (bool)(pProp->tccDriver ? true : false);
#else
return (bool)(pProp->major >= );
#endif
} int main(int argc, char **argv)
{
printf("\n\tStarting\n", argv[]); // 检查是否使用 64 位操作系统环境
if (sizeof(void*) != )
{
printf("\n\tError for program only supported with 64-bit OS and 64-bit target\n");
return EXIT_WAIVED;
} // 找到头两块计算能力不小于 2.0 的设备
int gpu_n;
cudaGetDeviceCount(&gpu_n);
printf("\n\tDevice count: %d\n", gpu_n);
if (gpu_n < )
{
printf("\n\tError for two or more GPUs with SM2.0 required\n");
return EXIT_WAIVED;
} cudaDeviceProp prop[MAX_GPU_COUNT];
int gpuid[MAX_GPU_COUNT], gpu_count = ;
printf("\n\tShow device\n");// 展示所有设备
for (int i=; i < gpu_n; i++)
{
cudaGetDeviceProperties(&prop[i], i);
if ((prop[i].major >= )
#ifdef _WIN32
&& prop[i].tccDriver// Windows 系统还要求有 Tesla 计算集群驱动
#endif
)
gpuid[gpu_count++] = i;
printf("\n\tGPU%d = \"%15s\" ---- %s\n", i, prop[i].name, (IsGPUCapableP2P(&prop[i]) ? "YES" : "NO"));
}
if (gpu_count < )
{
printf("\n\tError for two or more GPUs with SM2.0 required\n");
#ifdef _WIN32
printf("\nOr for TCC driver required\n");
#endif
cudaSetDevice();
return EXIT_WAIVED;
} // 寻找测试设备
int can_access_peer, p2pCapableGPUs[];
p2pCapableGPUs[] = p2pCapableGPUs[] = -;
printf("\n\tShow combination of devices with P2P\n");// 展示所有能 P2P 的设备组合
for (int i = ; i < gpu_count - ; i++)
{
for (int j = i + ; j < gpu_count; j++)
{
cudaDeviceCanAccessPeer(&can_access_peer, gpuid[i], gpuid[j]);
if (can_access_peer)
{
printf("\n\tGPU%d (%s) <--> GPU%d (%s) : %s\n", gpuid[i], prop[gpuid[i]].name, gpuid[j], prop[gpuid[j]].name);
if (p2pCapableGPUs[] == -)
p2pCapableGPUs[] = gpuid[i], p2pCapableGPUs[] = gpuid[j];
}
}
}
if (p2pCapableGPUs[] == - || p2pCapableGPUs[] == -)
{
printf("\n\tError for P2P not available among GPUs\n");
for (int i=; i < gpu_count; i++)
cudaSetDevice(gpuid[i]);
return EXIT_WAIVED;
} // 使用找到的设备进行测试
gpuid[] = p2pCapableGPUs[];
gpuid[] = p2pCapableGPUs[];
printf("\n\tEnabling P2P between GPU%d and GPU%d\n", gpuid[], gpuid[]); // 启用 P2P
cudaSetDevice(gpuid[]);
cudaDeviceEnablePeerAccess(gpuid[], );
cudaSetDevice(gpuid[]);
cudaDeviceEnablePeerAccess(gpuid[], ); // 检查设备是否支持同一可视地址空间 (Unified Virtual Address Space,UVA)
if (!(prop[gpuid[]].unifiedAddressing && prop[gpuid[]].unifiedAddressing))
printf("\n\tError for GPU not support UVA\n");
return EXIT_WAIVED; // 申请内存
const size_t buf_size = * * * sizeof(float);
printf("\n\tAllocating buffers %iMB\n", int(buf_size / / ));
cudaSetDevice(gpuid[]);
float *g0;
cudaMalloc(&g0, buf_size);
cudaSetDevice(gpuid[]);
float *g1;
cudaMalloc(&g1, buf_size);
float *h0;
cudaMallocHost(&h0, buf_size); cudaEvent_t start_event, stop_event;
int eventflags = cudaEventBlockingSync;
float time_memcpy;
cudaEventCreateWithFlags(&start_event, eventflags);
cudaEventCreateWithFlags(&stop_event, eventflags);
cudaEventRecord(start_event, ); for (int i=; i<; i++)
{
// GPU 互拷
// UVA 特性下 cudaMemcpyDefault 直接根据指针(属于主机还是设备)来确定拷贝方向
if (i % == )
cudaMemcpy(g1, g0, buf_size, cudaMemcpyDefault);
else
cudaMemcpy(g0, g1, buf_size, cudaMemcpyDefault);
}
cudaEventRecord(stop_event, );
cudaEventSynchronize(stop_event);
cudaEventElapsedTime(&time_memcpy, start_event, stop_event);
printf("\n\tcudaMemcpy: %.2fGB/s\n", (100.0f * buf_size) / (1024.0f * 1024.0f * 1024.0f * (time_memcpy / 1000.0f))); for (int i=; i<buf_size / sizeof(float); i++)
h0[i] = float(i % );
cudaSetDevice(gpuid[]);
cudaMemcpy(g0, h0, buf_size, cudaMemcpyDefault); const dim3 threads(, );
const dim3 blocks((buf_size / sizeof(float)) / threads.x, ); // 使用 GPU1 读取 GPU0 的全局内存数据,计算并写入 GPU1 的全局内存
printf("\n\tRun kernel on GPU%d, reading data from GPU%d and writing to GPU%d\n", gpuid[], gpuid[], gpuid[]);
cudaSetDevice(gpuid[]);
SimpleKernel<<<blocks, threads>>>(g0, g1);
cudaDeviceSynchronize(); // 使用 GPU0 读取 GPU1 的全局内存数据,计算并写入 GPU0 的全局内存
printf("\n\tRun kernel on GPU%d, reading data from GPU%d and writing to GPU%d\n", gpuid[], gpuid[], gpuid[]);
cudaSetDevice(gpuid[]);
SimpleKernel<<<blocks, threads>>>(g1, g0);
cudaDeviceSynchronize(); // 检查结果
cudaMemcpy(h0, g0, buf_size, cudaMemcpyDefault);
int error_count = ;
for (int i=; i<buf_size / sizeof(float); i++)
{
if (h0[i] != float(i % ) * 2.0f * 2.0f)
{
printf("\n\tResult error at %i: gpu[i] = %f, cpu[i] = %f\n", i, h0[i], (float(i%)*2.0f*2.0f));
if (error_count++ > )
break;
}
} // 关闭 P2P
cudaSetDevice(gpuid[]);
cudaDeviceDisablePeerAccess(gpuid[]);
cudaSetDevice(gpuid[]);
cudaDeviceDisablePeerAccess(gpuid[]); // 回收工作
cudaFreeHost(h0);
cudaSetDevice(gpuid[]);
cudaFree(g0);
cudaSetDevice(gpuid[]);
cudaFree(g1);
cudaEventDestroy(start_event);
cudaEventDestroy(stop_event);
for (int i=; i<gpu_n; i++)
cudaSetDevice(i);
printf("\n\t%s!\n",error_count?"Test failed": "Test passed"); getchar();
return ;
}

▶ 输出结果

只有一台设备,暂无法进行测试

▶ 涨姿势:

● P2P 要求:至少两台计算能力不低于 2.0 的设备,并支持同一可视内存空间特性;计算环境不低于 CUDA 4.0;Windows 安装 Tesla 计算集群驱动。

● 使用P2P的关键步骤

 // 检查两台设备之间是否能使用 P2P
int can_access_peer;
cudaDeviceCanAccessPeer(&can_access_peer, gpuid[i], gpuid[j])); // 启用 P2P
cudaSetDevice(gpuid[i]);
cudaDeviceEnablePeerAccess(gpuid[j], );
cudaSetDevice(gpuid[j];
cudaDeviceEnablePeerAccess(gpuid[i], ); // 设备间传输数据
cudaMemcpy(g1, g0, buf_size, cudaMemcpyDefault); // 关闭 P2P
cudaSetDevice(gpuid[i]);
cudaDeviceDisablePeerAccess(gpuid[i]);
cudaSetDevice(gpuid[j]);
cudaDeviceDisablePeerAccess(gpuid[j]); // cuda_runtime_api.h
extern __host__ cudaError_t CUDARTAPI cudaDeviceCanAccessPeer(int *canAccessPeer, int device, int peerDevice); extern __host__ cudaError_t CUDARTAPI cudaDeviceEnablePeerAccess(int peerDevice, unsigned int flags); extern __host__ cudaError_t CUDARTAPI cudaDeviceDisablePeerAccess(int peerDevice);

● 其他代码中的定义

 // helper_string.h
#define EXIT_WAIVED 2

0_Simple__simpleP2P的更多相关文章

    随机推荐

    1. Eclipse 离线安装ADT

      由于小猪的Eclipse中ADT版本过低无法打开Android项目了,所以要更新ADT(Android Develop Tools),但是在国内由于谷歌与本国微妙的关系想自动安装总是卡在某个点上. 所 ...

    2. java7新特性 java8新特性

      Java 7 的7个新特性 Java7语法新特性 JAVA8 十大新特性详解 http://www.jb51.net/article/48304.htm

    3. PowerMockito使用详解(转)

      一.为什么要使用Mock工具 在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,远程服务, 文件系统等等). 而我们没法控制这些外部依赖的对象,为了解 ...

    4. uboot中raise:Signal &num;8 caught的根本原因

      在移植uboot时编译一切正常,但uboot启动中载入自己写的网卡驱动出现故障,一直在打印raise:Signal #8 caught google  百度了一番,也有非常多人遇到了这个问题,大家都说 ...

    5. libev学习笔记

      转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...

    6. HDU1560 DNA sequence IDA&ast; &plus; 强力剪枝 &lbrack;kuangbin带你飞&rsqb;专题二

      题意:给定一些DNA序列,求一个最短序列能够包含所有序列. 思路:记录第i个序列已经被匹配的长度p[i],以及第i序列的原始长度len[i].则有两个剪枝: 剪枝1:直接取最长待匹配长度.1900ms ...

    7. HttpClient使用代理IP

      在爬取网页的时候,有的网站会有反爬虫措施,导致服务器请求拒接,可以使用代理IP来访问,解决请求拒绝的问题 代理IP分 透明代理.匿名代理.混淆代理.高匿代理 1.透明代理(Transparent Pr ...

    8. ABP-添加表

      1.在My_ABP.Core根目录下先创建一个文件夹,在该文件夹里创建一个公共的类,在里面定义所需要用到的属性 public class Person:Entity      {         pu ...

    9. C51学习笔记

      转自:http://blog.csdn.net/gongyuan073/article/details/7856878 单片机C51学习笔记 一,   C51内存结构深度剖析 二,   reg51.头 ...

    10. Mybatis的初步使用

      MyBatis 是当下最流行的持久层框架,也是ORM框架,本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google ...