Salta al contingut

    ↑ ↓ per moure't↵ per obrir

    Descomposició de tasques · 3.6

    Protegir problemes de concurrència

    Quan molts threads actualitzen la mateixa variable apareixen data races. Veuràs critical, els locks, atomic, la reducció manual i reduction, de menys a més eficient.

    Conceptes clau

    • Secció crítica (critical)
    • Locks (omp_lock_t)
    • Operació atòmica (atomic)
    • Reducció manual
    • Reducció automàtica (reduction)
    Prova-hoCondició de carrera (data race)
    memòriax = 0

    En càlcul paral·lel és habitual acumular resultats parcials en una variable compartida (per exemple, un producte escalar). Múltiples threads accedint i modificant simultàniament la mateixa variable provoquen data races. OpenMP ofereix diversos mecanismes per gestionar-ho, ordenats de menor a major eficiència.

    Secció crítica (critical)

    Garanteix que només un thread executa el bloc cada vegada. És la solució més general però la menys eficient, ja que serialitza tots els accessos, fins i tot quan no hi ha conflicte real.

    #pragma omp parallel
    {
        int val = compute_local(...);
        #pragma omp critical
        {
            insert_sorted(shared_list, val); // operacio arbitraria
        }
    }

    Locks

    Ofereixen un control més fi que critical. En lloc de bloquejar globalment un bloc de codi, cada lock s’associa a un recurs concret (per exemple, una posició d’una taula hash), permetent que threads treballin en paral·lel sobre recursos independents. Si tots accedeixen al mateix recurs, el rendiment serà similar a critical.

    Per utilitzar-los cal declarar variables de tipus omp_lock_t, inicialitzar-les i destruir-les:

    • omp_init_lock(&lock): inicialitza el lock.
    • omp_set_lock(&lock): si el lock està lliure, el bloqueja i continua; si està ocupat, el thread queda bloquejat esperant.
    • omp_unset_lock(&lock): allibera el lock perquè un altre thread el pugui adquirir.
    • omp_destroy_lock(&lock): destrueix el lock.
    omp_lock_t v_locks[M]; // un lock per cada recurs
    for (int i = 0; i < M; i++)
        omp_init_lock(&v_locks[i]);
    
    #pragma omp parallel
    {
        while (...) {
            int index = ...;
            omp_set_lock(&v_locks[index]);   // bloqueja index
            function(v[index], ...);         // acces protegit
            omp_unset_lock(&v_locks[index]); // desbloqueja index
        }
    }
    
    for (int i = 0; i < M; i++)
        omp_destroy_lock(&v_locks[i]);

    Operació atòmica (atomic)

    Garanteix que una operació d’actualització simple sigui indivisible. Només funciona per a operacions bàsiques (+, -, *, /, &, |, ^, <<, >>). Menys sobrecost que critical, però genera una operació atòmica per cada actualització, de manera que amb molts threads sobre la mateixa variable la contenció pot ser elevada.

    int count = 0;
    #pragma omp parallel
    {
        // ... cada thread fa feina ...
        if (found_match(...)) {
            #pragma omp atomic
            count++; // actualitzacio atomica puntual
        }
    }

    Reducció manual

    Cada thread acumula en una variable local (privada) i fa una única operació atòmica al final per combinar el resultat parcial amb el global. Redueix les operacions atòmiques de NN iteracions al nombre de threads.

    int total = 0;
    #pragma omp parallel
    {
        int id = omp_get_thread_num();
        int nt = omp_get_num_threads();
        int BS = N / nt;
        int total_local = 0;
        for (int i = id * BS; i < (id + 1) * BS; i++) {
            total_local += v[i] * w[i];
        }
        #pragma omp atomic
        total += total_local; // una unica atomica per thread
    }

    Reducció automàtica (reduction)

    La clàusula reduction(op:var) és la solució més neta i eficient. OpenMP gestiona automàticament les còpies privades i les combina al final. Operadors suportats: +, -, *, /, &, |, ^, max, min.

    Per a tasques explícites amb task (no taskloop), cal usar task_reduction (al pare) + in_reduction (a cada tasca filla).

    int total = 0;
    #pragma omp parallel
    #pragma omp single
    {
        #pragma omp taskloop reduction(+: total)
        for (int i = 0; i < N; i++)
            total += v[i] * w[i]; // OpenMP gestiona les copies privades
    }

    Comparativa

    Mètode Sobrecost Escalabilitat Ús típic
    critical Molt alt Baixa Accés genèric a recursos
    Locks Alt Mitjana Recursos independents (taula hash)
    atomic Mitjà Baixa Operacions simples sobre una variable
    Reducció manual Baix Alta Càlculs acumulatius
    Reducció automàtica Baix Alta Sumes, màxims, mínims…