Gestion du cache pour la multiplication masortingcielle fragmentée à l’aide d’OpenMP

J’ai des problèmes avec ce que je pense être une fausse mise en cache, je n’obtiens qu’une petite accélération lorsque j’utilise le code suivant par rapport à la version non parallèle

masortingx1 et masortingx2 sont des masortingces creuses dans une structure au format (ligne, colonne, valeur).

void pMultiply(struct SparseRow *masortingx1, struct SparseRow *masortingx2, int m1Rows, int m2Rows, struct SparseRow **result) { *result = malloc(1 * sizeof(struct SparseRow)); int resultNonZeroEnsortinges = 0; #pragma omp parallel for atomic for(int i = 0; i < m1Rows; i++) { int curM1Row = matrix1[i].row; int curM1Col = matrix1[i].col; float curM1Value = matrix1[i].val; for(int j = 0; j < m2Rows; j++) { int curM2Row = matrix2[j].row; int curM2Col = matrix2[j].col; float curM2Value = matrix2[j].val; if(curM1Col == curM2Row) { *result = realloc(*result, (sizeof(struct SparseRow)*(resultNonZeroEntries+1))); (*result)[resultNonZeroEntries].row = curM1Row; (*result)[resultNonZeroEntries].col = curM2Col; (*result)[resultNonZeroEntries].val += curM1Value*curM2Value; resultNonZeroEntries++; break; } } } 

Plusieurs problèmes ici:

  • Comme mentionné par Brian Brochers, la clause #pragma omp atomic devrait être placée juste avant la ligne à protéger contre une situation de #pragma omp atomic .
  • La réaffectation de la mémoire à chaque étape est probablement un facteur de perte de performances. Si la mémoire ne peut pas être réaffectée sur place et doit être copiée ailleurs, la procédure sera lente. C’est également une source d’erreurs, car la valeur du result du pointeur est modifiée. D’autres threads continuent à s’exécuter alors que la réallocation a lieu et peuvent essayer d’accéder à la mémoire à la “vieille” adresse ou plusieurs threads peuvent essayer de réallouer les results simultanément. Placer l’intégralité de la partie realloc + addition dans une section critique serait plus sûr, mais sérialiserait la fonction pour tout, sauf le test d’égalité des index de ligne / colonne au prix d’une surcharge importante. Les threads doivent fonctionner avec le tampon local, puis fusionner leurs résultats ultérieurement. La réaffectation doit être effectuée par blocs de taille suffisante.

     // Make sure this will comstack even without openmp + include memcpy #include  #ifdef _OPENMP #define thisThread omp_thread_num() #define nThreads omp_num_threads() #else #define thisThread 0 #define nThreads 1 #endif // shared variables int totalNonZero,*copyIndex,*threadNonZero; #pragma omp parallel { // each thread now initialize a local buffer and local variables int localNonZero = 0; int allocatedSize = 1024; SparseRow *localResult = malloc(allocatedSize * sizeof(*SparseRow)); // one thread initialize an array #pragma omp single { threadNonZero=malloc(nThreads*sizeof(int));copyIndex=malloc((nThreads+1)*sizeof(int)); } #pragma omp for for (int i = 0; i < m1Rows; i++){ /* * do the same as your initial code but: * realloc an extra 1024 lines each time localNonZeros exceeds allocatedSize * fill the local buffer and increment the localNonZeros counter * this is safe, no need to use critical / atomic clauses */ } copyIndex[thisThread]=localNonZero; //put number of non zero into a shared variable #pragma omp barrier // Wrap_up : check how many non zero values for each thread, allocate the output and check where each thread will copy its local buffer #pragma omp single { copyIndex[0]=0; for (int i=0; i 
  • Veuillez noter que l’algorithme générera des doublons, par exemple si la première masortingce contient des valeurs aux positions (1,10) et (1,20) et la seconde (10,5) et (20,5), il y aura deux (1 , 5) lignes dans le résultat. À un moment donné, une fonction de compactage qui fusionne les lignes en double sera nécessaire.