在多GPU上启动异步内存复制操作

时间:2015-01-13 12:03:16

标签: c++ cuda multi-gpu

我想分开&将主机上的数据数据复制到多个gpus的设备存储器中。另外,我想同时进行所有这些复制操作。

为此,我使用cudaMemcpyAsync,我在每个GPU的私有流中启动。

以下是我正在做的事情(代码中的疑问标有以?开头的评论)

#define SIZE 1000
#define GPUCOUNT 2

int* hostData = nullptr;
int *devData[GPUCOUNT];
cudaStream_t stream[GPUCOUNT];

// Create one stream per GPU
for ( int i=0; i != GPUCOUNT ; ++i )
{    
    // DO I need to call cudaSetDevice before creating stream for each GPU ??
    cudaStreamCreate(&stream[i]));
}

// Allocate pinned data on host
cudaMallocHost (&hostData, SIZE );

// Allocate data on each device and copy part of host data to it
for( int i=0; i != GPUCOUNT ; ++i )
{
   cudaSetDevice(i);
   cudaMalloc( (void**) &devData[i], sizeof(int) * SIZE/GPUCOUNT  ); // ?? Does blocking behavior of cudamalloc prevents asynch memcpy invoked in stream of other GPUs from running concurrently 
   cudaMemcpyAsync( (void*) devData[i], hostData + i*SIZE/GPUCOUNT, SIZE/GPUCOUNT, cudaMemcpyHostToDevice, stream[i] );
}

// Some CPU code while copy is happening
// ....

// Wait for copy on all streams to finish
cudaDeviceSynchronize();

// Do something else

当我阅读C编程指南时,我看到上述mem复制操作不会异步发生,因为在两个连续的异步内存复制启动之间,我正在调用一个分配设备内存的主机操作(阻塞调用)。

  

3.2.5.5.4。隐式同步

     

来自不同流的两个命令无法同时运行(如果有的话)   以下操作由主机在它们之间发出   螺纹:

     

page一个页面锁定的主机内存分配,

     

device设备内存分配

     

device设备内存集

     

‣两个地址之间的内存复制到同一设备内存,

     

‣任何CUDA命令到默认流

如果上述原因似乎属实,那么我需要拆分内存分配和复制操作

// Allocate data on each device 
for( int i=0; i != GPUCOUNT ; ++i )
{
   cudaSetDevice(i);
   cudaMalloc( (void**) &devData[i], sizeof(int) * SIZE/GPUCOUNT  );
}

// Copy part of host data to each device
for( int i=0; i != GPUCOUNT ; ++i )
{
   // ?? DO I need to call cudaSetDevice before memory copy ??
   // CUDA guide says:"A memory copy will succeed even if it is issued to a stream that is not associated to the current device."

   cudaMemcpyAsync( (void*) devData[i], hostData + i*SIZE/GPUCOUNT, SIZE/GPUCOUNT, cudaMemcpyHostToDevice, stream[i] ); 
}

我的上述分析是否有效?

另外,如果不通过在每个GPU的默认流(流ID 0)中启动cudaMemcpyAsync操作来创建显式的每个gpu流,是不是可以这样做?。我的理由是基于CUDA C编程指南中的声明:

  

每个设备都有自己的默认流(请参阅默认流),所以   发布到设备的默认流的命令可以执行   订单或同时发出的默认命令   任何其他设备的流。

代码看起来像这样

#define SIZE 1000
#define GPUCOUNT 2

int* hostData = nullptr;
int *devData[GPUCOUNT];

// Allocate pinned data on host
cudaMallocHost (&hostData, SIZE );

// Allocate data on each device
for( int i=0; i != GPUCOUNT ; ++i )
{
   cudaSetDevice(i);
   cudaMalloc( (void**) &devData[i], sizeof(int) * SIZE/GPUCOUNT  );
}

// Copy part of host data to each device
for( int i=0; i != GPUCOUNT ; ++i )
{
   // ?? DO I need to call cudaSetDevice before memory copy ??
   // CUDA guide says:"A memory copy will succeed even if it is issued to a stream that is not associated to the current device."

   cudaMemcpyAsync( (void*) devData[i], hostData + i*SIZE/GPUCOUNT, SIZE/GPUCOUNT, cudaMemcpyHostToDevice, 0 ); 
}

// Some CPU code while copy is happening
// ....

// Wait for copy on all streams to finish
cudaDeviceSynchronize();

// Do something else

1 个答案:

答案 0 :(得分:1)

http://developer.download.nvidia.com/compute/cuda/4_1/rel/toolkit/docs/online/group__CUDART__DEVICE_g418c299b069c4803bfb7cab4943da383.html

cudaError_t cudaSetDevice   (   int     device   )      
  

将device设置为调用主机线程的当前设备。

     

随后从此主机线程使用分配的任何设备内存   cudaMalloc(),cudaMallocPitch()或cudaMallocArray()将是   物理上驻留在设备上。从中分配的任何主机内存   主机线程使用cudaMallocHost()或cudaHostAlloc()或   cudaHostRegister()将使其生命周期与设备相关联。任何   从此主机线程创建的流或事件将被关联   与设备。使用该主机线程从该主机线程启动的任何内核   <<<>>> operator或cudaLaunch()将在设备上执行。

     

此调用可以从任何主机线程,任何设备以及任何设备进行   时间。此功能不会与之前的或   新设备,应该被认为是一个非常低的开销呼叫。

看起来set device会在没有溪流的情况下完成所需的一切。您应该能够翻转每个设备,使用默认流并调用malloc和memcpy。使用异步memcpy和基于流的内核调用将有助于设备上的并发内存传输和内核调用。

您需要在调用该设备之前调用setdevice。 Streams不会对此有所帮助。