2017-01-30 5 views
0

Ich habe versucht, mein eigenes Mergesort zu implementieren, basierend auf von unten nach oben/iterativer mergesort Algorithmus. Dieser Algorithmus teilt die Daten nach 2 Elementen und sortiert sie. Dann um 4 Elemente und sortiert und so weiter, bis alle Daten sortiert sind. Also, mein Plan ist jedem Thread durch 2 Elemente zuzuordnen. So mache ich das:Mergesort CUDA

__global__ void mergeBU(int *d_a, int *d_aux, int sz, int N) 
{ 
    int idk = blockIdx.x*blockDim.x+threadIdx.x; 
    int lo = 2 * sz * idk; 
    int mid = lo + sz - 1; 
    float hi = fminf(lo + sz + sz - 1, N - 1); 
    merge(d_a, d_aux, lo, mid, hi); 
} 

__device__ void merge(int *d_a, int *d_aux, int lo, int mid, float hi) 
{ 
int i = lo; 
int j = mid + 1; 

    for (int k = lo; k <= hi; k++) 
    { 
     d_aux[k] = d_a[k]; 
    } 

    for (int k = lo; k <= hi; k++) 
    { 
     if (i > mid)     { d_a[k] = d_aux[j]; j++; } 
     else if (j > hi)    { d_a[k] = d_aux[i]; i++; } 
     else if (d_aux[j] < d_aux[i]) { d_a[k] = d_aux[j]; j++; } 
     else        { d_a[k] = d_aux[i]; i++; } 
    } 
} 

Sagen wir, ich meine Kernel aufrufen < < < 2,4 >>> (die 8 Threads ist), so kann ich nur Art 16 Elemente max. Wenn ich 32 Elemente eingabe, bleiben die restlichen Datenindexe unberührt (16-31). Wie kann der Thread-Index weiterhin den Rest des Datenindex verarbeiten? Mit fortfahren meine ich, dass der Thread-Index (0,1,2,3,4,5,6,7) den Rest des Datenindex weiter verarbeitet, er sollte wie threadindex (dataindex, dataindex) sein -> 0 (16, 17); 1 (18,19); 2 (20,21); und so weiter. Jeder Kommentar ist willkommen.

Antwort

1

Ohne an Ihrem eigentlichen Code zu suchen: Sortierung Merge ist ein Multi-Pass-Algorithmus. Da verschiedene Blöcke beim Ausführen eines Kernels normalerweise nicht synchronisiert werden (es sei denn, Sie verwenden atomare Einheiten), sollten Sie mehrere aufeinanderfolgende Kernel-Starts in Erwägung ziehen, einen für jeden Durchgang. Zum Beispiel sortiert beim ersten Start jeder Threadblock n_1 Elemente; Beim zweiten Start verbindet jedes Blockpaar 2 * n_1 Elemente und so weiter. Das ist natürlich nicht so einfach, wie es sich anhört: Wie kann man sagen, welcher Block was genau tun soll?

Auch möchten Sie vielleicht am approach used in the ModernGPU library für andere Ideen einen Blick haben.

0

Ihr Ansatz besteht darin, ein Array der Größe n in n/2 Sub-Arrays aufzuteilen, Paare dieser Sub-Arrays zu kombinieren, um schließlich mit n/4 Sub-Arrays zu enden und so weiter. Dieser Ansatz würde jedoch wahrscheinlich Speicherbandbreite begrenzt sein.

Angenommen, Sie verwenden 8 "Gewinde". Teilen Sie das Array in 8 Sub-Arrays der Größe n/8 auf (das letzte Sub-Array kann eine andere Größe haben), verwenden Sie dann 8 Threads, um das Sub-Array zu sortieren, und 4 Threads, um die 4 Paare des sortierten Subs zusammenzuführen - Arrays, dann 2 Threads zum Zusammenführen der 2 Paare von zusammengefügten Sub-Arrays, dann 1 Thread zum Zusammenführen der letzten 2 Paare.

Basierend auf meiner Erfahrung mit einer Multi-Thread-Sortierung, erreichen Sie die Speicherbandbreite bei 8 Threads für eine CPU, aber wenn der GPU-Speicher verwendet werden kann, um große Teile des Arrays zu halten, dann mehr als 8 Threads nützlich sein. Ich weiß nicht, welche Operationen (vergleichen, bewegen, ...) in der GPU und ihrem Speicher möglich sind.

+0

Mein schlechter Herr, was ich gemeint ist, wird Arraydaten durch zwei Elemente auf dem ersten Iteration gespaltet, so dass die Unter Arrays enthält zwei Elemente jeweils, dann jeden Sub-Array sortierten, dann nach 4 Elementen dann sortierten und so weiter. Ich habe meine Frage bearbeitet. Ihr Rat scheint gut zu sein, aber der Thread hat einen Index, wie man jeden Thread einem Array von Daten-Indexen zuordnet, mit denen ich es zu tun habe. Da der Thread begrenzt ist, sind meine Daten auch begrenzt, wenn ich eine feste Anzahl von Elementen pro Thread habe. –

+0

@PriyanggaJanmantaraAnusasana - verwenden Sie eine Array-Größe von 2048 als Beispiel. In meiner Version, die 8 Threads verwendet, führt jeder Thread eine Merge-Sortierung nach 256 Elementen durch, wobei jeweils 8 Durchläufe ausgeführt werden, wobei jeder Durchlauf Läufe der Größe 2, 4, 8, 16, 32, 64, 128, 256 erzeugt Läufe der Größe 256, um 4 Läufe der Größe 512 zu erstellen. Dann führen 2 Läufe Paare von Läufen der Größe 512 zusammen, um 2 Läufe der Größe 1024 zu erstellen. Dann führt 1 Thread die letzten zwei Läufe zu einem Lauf der Größe 2048 zusammen. – rcgldr

+0

@ PriyanggaJanmantaraAnusasana - Fortsetzung, was Sie vorschlagen, ist 1024 Threads laufen zu lassen, um Läufe der Größe 2 zu erstellen, dann 512 Threads, um Läufe der Größe 4 zu erstellen, 256 Threads, um Läufe der Größe 8 zu erzeugen, ... und so weiter. Die Threads würden in sequenziellen Blöcken laufen, vielleicht 8 oder 16 Threads gleichzeitig laufen, aber wie ich in meiner Antwort erwähnte, ist die Speicherbandbreite auf einem typischen PC bei ungefähr 8 Threads erreicht. – rcgldr