如何在cuda中的不同GPU之间复制内存

如何在cuda中的不同GPU之间复制内存,cuda,multi-gpu,Cuda,Multi Gpu,目前,我与两个GTX650的工作。我的程序类似于简单的客户机/服务器结构。我在两个GPU上分配工作线程。服务器线程需要从客户端线程收集结果向量,因此我需要在两个gpu之间复制内存。不幸的是,cuda示例中的简单P2P程序无法工作,因为我的卡没有TCC驱动程序。花了两个小时在谷歌上搜索,所以我找不到答案。一些消息来源说我应该使用cudaMemcpyPeer,而另一些消息来源说我应该使用cudaMemcpy和cudaMemcpyDefault。除了复制到主机然后复制到设备之外,还有什么简单的方法来完

目前,我与两个GTX650的工作。我的程序类似于简单的客户机/服务器结构。我在两个GPU上分配工作线程。服务器线程需要从客户端线程收集结果向量,因此我需要在两个gpu之间复制内存。不幸的是,cuda示例中的简单P2P程序无法工作,因为我的卡没有TCC驱动程序。花了两个小时在谷歌上搜索,所以我找不到答案。一些消息来源说我应该使用
cudaMemcpyPeer
,而另一些消息来源说我应该使用
cudaMemcpy
cudaMemcpyDefault
。除了复制到主机然后复制到设备之外,还有什么简单的方法来完成我的工作吗。我知道一定在什么地方有记录,但我找不到。谢谢你的帮助

将数据从一个GPU传输到另一个GPU通常需要通过主机内存进行“暂存”。例外情况是,GPU和系统拓扑支持对等(P2P)访问,并且P2P已明确启用。在这种情况下,数据传输可以直接通过PCIE总线从一个GPU传输到另一个GPU

在任何一种情况下(有或没有P2P可用/启用),典型的情况都是
cudamemcypeer
/
cudamemcypeerasync
,如cuda p2pBandwidthLatencyTest中所示

在windows上,P2P的一个要求是两个设备都由TCC模式下的驱动程序支持。在大多数情况下,TCC模式不是GeForce GPU的可用选项(最近,GeForce Titan系列GPU使用CUDA 7.5RC工具包中提供的驱动程序和运行时,出现了一个例外。)

因此,在Windows上,这些GPU将无法利用直接P2P传输。然而,几乎相同的序列可用于传输数据。CUDA运行时将检测传输的性质,并在“引擎盖下”执行分配以创建暂存缓冲区。然后,传输将分两部分完成:从原始设备到暂存缓冲区的传输,以及从暂存缓冲区到目标设备的传输

下面是一个完整的示例,演示如何将数据从一个GPU传输到另一个GPU,同时利用P2P访问(如果可用):

$ cat t850.cu
#include <stdio.h>
#include <math.h>

#define SRC_DEV 0
#define DST_DEV 1

#define DSIZE (8*1048576)

#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)


int main(int argc, char *argv[]){

  int disablePeer = 0;
  if (argc > 1) disablePeer = 1;
  int devcount;
  cudaGetDeviceCount(&devcount);
  cudaCheckErrors("cuda failure");
  int srcdev = SRC_DEV;
  int dstdev = DST_DEV;
  if (devcount <= max(srcdev,dstdev)) {printf("not enough cuda devices for the requested operation\n"); return 1;}
  int *d_s, *d_d, *h;
  int dsize = DSIZE*sizeof(int);
  h = (int *)malloc(dsize);
  if (h == NULL) {printf("malloc fail\n"); return 1;}
  for (int i = 0; i < DSIZE; i++) h[i] = i;
  int canAccessPeer = 0;
  if (!disablePeer) cudaDeviceCanAccessPeer(&canAccessPeer, srcdev, dstdev);
  cudaSetDevice(srcdev);
  cudaMalloc(&d_s, dsize);
  cudaMemcpy(d_s, h, dsize, cudaMemcpyHostToDevice);
  if (canAccessPeer) cudaDeviceEnablePeerAccess(dstdev,0);
  cudaSetDevice(dstdev);
  cudaMalloc(&d_d, dsize);
  cudaMemset(d_d, 0, dsize);
  if (canAccessPeer) cudaDeviceEnablePeerAccess(srcdev,0);
  cudaCheckErrors("cudaMalloc/cudaMemset fail");
  if (canAccessPeer) printf("Timing P2P transfer");
  else printf("Timing ordinary transfer");
  printf(" of %d bytes\n", dsize);
  cudaEvent_t start, stop;
  cudaEventCreate(&start); cudaEventCreate(&stop);
  cudaEventRecord(start);
  cudaMemcpyPeer(d_d, dstdev, d_s, srcdev, dsize);
  cudaCheckErrors("cudaMemcpyPeer fail");
  cudaEventRecord(stop);
  cudaEventSynchronize(stop);
  float et;
  cudaEventElapsedTime(&et, start, stop);
  cudaSetDevice(dstdev);
  cudaMemcpy(h, d_d, dsize, cudaMemcpyDeviceToHost);
  cudaCheckErrors("cudaMemcpy fail");
  for (int i = 0; i < DSIZE; i++) if (h[i] != i) {printf("transfer failure\n"); return 1;}
  printf("transfer took %fms\n", et);
  return 0;
}

$ nvcc -arch=sm_20 -o t850 t850.cu
$ ./t850
Timing P2P transfer of 33554432 bytes
transfer took 5.135680ms
$ ./t850 disable
Timing ordinary transfer of 33554432 bytes
transfer took 7.274336ms
$
$cat t850.cu
#包括
#包括
#定义SRC\u DEV 0
#定义DST_DEV 1
#定义DSIZE(8*1048576)
#定义cudaCheckErrors(msg)\
做{\
cudaError\u t\u err=cudaGetLastError()\
如果(_err!=cudaSuccess){\
fprintf(标准,“致命错误:%s(%s位于%s:%d)\n”\
msg,cudaGetErrorString(_err)\
__文件(行)\
fprintf(stderr,“***失败-中止\n”)\
出口(1)\
} \
}而(0)
int main(int argc,char*argv[]){
int disablePeer=0;
如果(argc>1)disablePeer=1;
国际发展账户;
cudaGetDeviceCount(&devcount);
CUDACHECKERS(“cuda故障”);
int srcdev=SRC_DEV;
int-dstdev=DST_-DEV;

如果(devcount),我很确定答案是你不能。你要么需要有支持GPU的TTC驱动模式(Telsa或Quadro),或切换到64位linuxSo我唯一的选择是通过cudaMallocHost维护cpu缓冲区,然后从中复制并复制回,对吗?如果切换到linux,在不同GPU之间复制的正确方式是什么?cuda示例代码演示了如何在两个设备之间执行memcpy操作。此代码旨在使用P2P机制(如果可用)le,否则使用“回退”路径。它不需要显式维护CPU缓冲区,但这样的缓冲区将“在引擎盖下”创建如果驱动程序使用了回退路径,@talonmies给出的注释是正确的。请注意,该示例代码使用的唯一
cudaMemcpy
类型操作是
cudamemcypeerasync
。此调用可以使用P2P路径(如果该路径可用且已启用),或者使用“回退”路径如果没有。正如@Talonmes所指出的,您需要一个合适的P2P环境才能直接从一个设备复制到另一个设备。没有它,复制将在主机内存中来回移动(尽管从
cudamemcypeerasync
调用看不出这一点-它是由驱动程序在引擎盖下处理的。)好的,考虑到客户端和服务器之间交换的数据仅为10000浮点/双精度,
cudamemcypeerasync
就足够了。