我想分开&将主机上的数据数据复制到多个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
答案 0 :(得分:1)
cudaError_t cudaSetDevice ( int device )
将device设置为调用主机线程的当前设备。
随后从此主机线程使用分配的任何设备内存 cudaMalloc(),cudaMallocPitch()或cudaMallocArray()将是 物理上驻留在设备上。从中分配的任何主机内存 主机线程使用cudaMallocHost()或cudaHostAlloc()或 cudaHostRegister()将使其生命周期与设备相关联。任何 从此主机线程创建的流或事件将被关联 与设备。使用该主机线程从该主机线程启动的任何内核 <<<>>> operator或cudaLaunch()将在设备上执行。
此调用可以从任何主机线程,任何设备以及任何设备进行 时间。此功能不会与之前的或 新设备,应该被认为是一个非常低的开销呼叫。
看起来set device会在没有溪流的情况下完成所需的一切。您应该能够翻转每个设备,使用默认流并调用malloc和memcpy。使用异步memcpy和基于流的内核调用将有助于设备上的并发内存传输和内核调用。
您需要在调用该设备之前调用setdevice。 Streams不会对此有所帮助。