2017-08-08 2 views
0

Kopiert CUDA 8.0 cudaMemcpy() gleichzeitig einen ganzen Speicherblock oder byteweise?CUDA 8.0 - cudaMemcpy() - lineare oder konstante Zeitoperation?

Ich möchte die Kopierzeit begrenzen, aber ich kann nichts in der Dokumentation finden, die angibt, ob cudaMemcpy() eine lineare oder konstante Zeitoperation ist.

+0

Je mehr Daten übertragen werden, desto länger dauert die Übertragung im Allgemeinen. Sie werden sicherlich kleine Abweichungen vom * perfekt linearen * Verhalten finden, aber auf einer hohen Ebene hat die Übertragung eine bestimmte Geschwindigkeit in Bytes/s, die damit verbunden ist, und diese Geschwindigkeit ist für größere Übertragungen ungefähr konstant. Sie können dies mithilfe eines der CUDA-Profiler oder durch das Timing einer bestimmten Übertragung mithilfe verschiedener Timing-Methoden herausfinden. Für kleine Übertragungen hat die Übertragung eine Charakteristik, die ungefähr eine feste Zeit ("Latenz") plus eine lineare Komponente ist. –

+0

Danke! Ich werde versuchen, mehr in den Übertragungsprozess zu lesen –

+0

Verkürzte den Titel und verbesserte die Formatierung, um es leserfreundlicher zu machen. – Fabien

Antwort

1

Synchrone Speicherübertragungen sind keine konstante Zeit, sondern haben sowohl eine feste Latenzkomponente als auch eine Komponente, die proportional zur Übertragungsgröße ist. Bei kleinen Größen dominiert die Latenz, bei großen Größen ist die begrenzende Übertragungsgeschwindigkeit durch die Speicher- oder Busbandbreite begrenzt.

Betrachten Sie das folgende triviale Benchmark:

#include <iostream> 
#include <string> 
#include <algorithm> 

__global__ void memsetkernel(int *x, int n) 
{ 
    int tid = threadIdx.x + blockIdx.x * blockDim.x; 
    int stride = blockDim.x * gridDim.x; 
    for(; tid < n; tid += stride) { 
     x[tid] = threadIdx.x; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    // size 
    int n = 100; 
    int nreps = 10; 

    if (argc > 1) { 
     n = std::stoi(std::string(argv[1])); 
    } 

    size_t sz = sizeof(int) * size_t(n); 

    // host array 
    int* host = new int[n]; 

    // allocate size ints on device 
    int* device; 
    cudaMalloc(&device, sz); 
    cudaEvent_t start, stop; 
    cudaEventCreate(&start); 
    cudaEventCreate(&stop); 

    { 
     int nthreads = 1024; 
     int nblocks = std::max(1, std::min(13*2, n/nthreads)); 
     memsetkernel<<<nblocks, nthreads>>>(device, n); 
     cudaDeviceSynchronize(); 
     cudaEventRecord(start); 
     for(int i=0; i<nreps; i++) { 
      memsetkernel<<<nblocks, nthreads>>>(device, n); 
     } 
     cudaEventRecord(stop); 
     cudaEventSynchronize(stop); 
     float milliseconds, kilobytes, bandwidth; 
     cudaEventElapsedTime(&milliseconds, start, stop); 
     milliseconds /= float(nreps); // Average of nreps 
     kilobytes = float(sz)/1e3f; 
     bandwidth = kilobytes/milliseconds;   
     std::cout << "kernel assignment: " << bandwidth << " Mb/s" << std::endl; 
    } 

    { 
     cudaMemcpy(host, device, sz, cudaMemcpyDeviceToHost); 
     cudaEventRecord(start); 
     for(int i=0; i<nreps; i++) { 
      cudaMemcpy(host, device, sz, cudaMemcpyDeviceToHost); 
     } 
     cudaEventRecord(stop); 
     cudaEventSynchronize(stop); 
     float milliseconds, kilobytes, bandwidth; 
     cudaEventElapsedTime(&milliseconds, start, stop); 
     milliseconds /= float(nreps); // Average of nreps 
     kilobytes = float(sz)/1e3f; 
     bandwidth = kilobytes/milliseconds;   
     std::cout << "DTOH: " << bandwidth << " Mb/s" << std::endl; 
    } 

    { 
     cudaMemcpy(device, host, sz, cudaMemcpyHostToDevice); 
     cudaEventRecord(start); 
     for(int i=0; i<nreps; i++) { 
      cudaMemcpy(device, host, sz, cudaMemcpyHostToDevice); 
     } 
     cudaEventRecord(stop); 
     cudaEventSynchronize(stop); 
     float milliseconds, kilobytes, bandwidth; 
     cudaEventElapsedTime(&milliseconds, start, stop); 
     milliseconds /= float(nreps); // Average of nreps 
     kilobytes = float(sz)/1e3f; 
     bandwidth = kilobytes/milliseconds; 
     std::cout << "HTOD: " << bandwidth << " Mb/s" << std::endl; 
    } 

    // reset device 
    cudaDeviceReset(); 

} 

des Lauf bei unterschiedlichen Datengrößen zeigt das folgende Verhalten:

enter image description here

Beiden Geräte-zu-Host und Host-to-device asymptotisch einen Wert von etwa 60% der Bandbreite des PCI-e-Busses der fraglichen Maschine erreichen (etwa 6,5 ​​Gb/s, höhere kann unter Verwendung eines gepinnten Host-Speichers erreicht werden), während der Kern etwa 70% der Hauptspeicherbandbreite erreicht die GPU (150 Gb/s mit einer theoretischen maximalen Bandbreite von etwa 224 Gb/s).

NVIDIA liefert ein Beispiel für die Messung der Übertragungsbandbreite, die Sie über here lesen können. Sie können dies verwenden, um die Leistung Ihrer Hardware für sich selbst zu erkunden.

+0

Vielen Dank! Aus irgendeinem Grund dachte ich, es wäre möglich, einen ganzen Speicherblock gleichzeitig zu kopieren und zu übertragen, wenn er innerhalb der Bandbreite liegt. Es hätte mir einfallen sollen, dass die Übertragung der Daten unweigerlich in kleinere Stücke zerfällt. –

+0

Beachten Sie, dass [Bandbreite] (http://www.wikipedia.org/wiki/Bandwidth_ (Computing)) eine Einheit von (Giga/Mega/Kilo) Bytes pro Sekunde hat und wenig mit der Breite der Kommunikation zu tun hat Kanal (am wahrscheinlichsten [16 Lanes] (https://en.wikipedia.org/wiki/PCI_Express) im Fall einer GPU). Stattdessen bezieht sich der Name auf die [spektrale Breite des Frequenzbands] (http://www.wikipedia.org/wiki/Bandwidth_ (signal_processing)), die zum Übertragen von Information verwendet wird. – tera

Verwandte Themen