2012-06-11 4 views
3

La réduction de grands tableaux peut être effectuée en appelant __reduce(); plusieurs fois.OpenCL/CUDA: Algorithme de réduction en deux étapes

Le code suivant utilise cependant que deux étapes et est documenté here:

Cependant, je suis incapable de comprendre l'algorithme de cette réduction en deux étapes. certains peuvent-ils donner une explication plus simple?

__kernel 
void reduce(__global float* buffer, 
     __local float* scratch, 
     __const int length, 
     __global float* result) { 

    int global_index = get_global_id(0); 
    float accumulator = INFINITY; 
    // Loop sequentially over chunks of input vector 
    while (global_index < length) { 
     float element = buffer[global_index]; 
     accumulator = (accumulator < element) ? accumulator : element; 
     global_index += get_global_size(0); 
    } 

    // Perform parallel reduction 
    int local_index = get_local_id(0); 
    scratch[local_index] = accumulator; 
    barrier(CLK_LOCAL_MEM_FENCE); 
    for(int offset = get_local_size(0)/2; offset > 0; offset = offset/2) { 
     if (local_index < offset) { 
      float other = scratch[local_index + offset]; 
      float mine = scratch[local_index]; 
      scratch[local_index] = (mine < other) ? mine : other; 
     } 
     barrier(CLK_LOCAL_MEM_FENCE); 
    } 
    if (local_index == 0) { 
     result[get_group_id(0)] = scratch[0]; 
    } 
} 

Il peut également être bien mis en œuvre en utilisant CUDA.

+1

Ce code de réduction parallèle est un port OpenCL simple de la réduction CUDA canonique (sans quelques optimisations que OpenCL ne peut pas faire). Il a été écrit par [Mark Harris] (http://stackoverflow.com/users/749748/harrism) de NVIDIA. Vous pouvez trouver un livre blanc très instructif sur l'algorithme dans le SDK CUDA [exemple de réduction] (http://developer.nvidia.com/cuda-cc-sdk-code-samples#reduction). Après avoir lu ce livre blanc, éditez votre question pour expliquer ce que vous ne comprenez pas et que quelqu'un pourrait vous aider. – talonmies

+0

Merci de montrer la bonne direction. Eh bien, je suis confronté à des difficultés à comprendre la différence entre le noyau de réduction en deux étapes et multi-étapes comme indiqué [ici] (http://developer.amd.com/Membership/Print.aspx?ArticleID=221&web=http://developer.amd .com/documentation/articles) – gpuguy

Répondre

4

Vous créez des threads N. Le premier thread regarde les valeurs aux positions 0, N, 2 * N, ... Le deuxième thread regarde les valeurs 1, N + 1, 2 * N + 1, ... C'est la première boucle. Il réduit les valeurs length en valeurs N. Puis chaque thread enregistre sa plus petite valeur dans la mémoire partagée/locale. Ensuite, vous avez une instruction de synchronisation (barrier(CLK_LOCAL_MEM_FENCE).) Ensuite, vous avez une réduction standard de la mémoire partagée/locale. Lorsque vous avez terminé, le thread avec l'ID local 0 enregistre son résultat dans le tableau de sortie. En résumé, vous avez une réduction de length à N/get_local_size(0). Vous auriez besoin de faire une dernière passe après l'exécution de ce code. Cependant, cela permet de faire la plus grande partie du travail, par exemple, vous pouvez avoir une longueur ~ 10^8, N = 2^16, get_local_size (0) = 256 = 2^8, et ce code réduit 10^8 éléments en 256 éléments .

Quelles parties ne comprenez-vous pas?

+0

Salut, moi aussi je suis coincé dans le processus de réduction. Eh bien, j'essaie d'implémenter des algorithmes de tri dans OpenCL. Le problème est, je peux appliquer confortablement réduction dans la mémoire locale, ce qui n'est pas une sueur pour beaucoup. Ce que je suis incapable de faire est de réduire/fusionner/combiner les tableaux triés présents dans le tableau local à un grand tableau entièrement trié. Offcourse je peux courir les algorithmes de tri sur la mémoire globale à la fin mais je ne pense pas que c'est la manière de faire. S'il vous plaît aidez-moi ...... – Yash

+1

J'ai eu mes problèmes à comprendre cela aussi. La façon dont je comprends ce noyau réduit le problème aux éléments 'get_num_groups (0)'. Ce qui serait 'get_global_size (0)/get_local_size (0)' Et est défini par le NDRange, qui est défini lorsque le noyau est mis en file d'attente. Le code hôte est donc essentiel pour ce noyau, mais malheureusement jamais fourni sur la page AMD d'origine. Je pense qu'un NDRange qui correspond 'CL_DEVICE_MAX_COMPUTE_UNITS * CL_DEVICE_MAX_WORK_GROUP_SIZE' ferait l'affaire du parallélisme maximal des parties séquentielles. Corrigez-moi si je me trompe. – Armin

Questions connexes