2013-04-23 9 views
6

Sto cercando di parallelizzare il ciclo nel seguente funzione con OpenMPriduzione OpenMP con operatore di overload

void CEnergymulti::forcetwobody(vector<CMolecule*> m_mols,CPnt force0,CPnt torque0) 
{ 
const int nmol=m_mols.size(); 
vector<CMolecule*> twomols(2); 
CPnt forcetemp,torquetemp; 
twomols.clear(); 
force0.zero(); 
torque0.zero(); 
forcetemp.zero(); 
torquetemp.zero(); 
#pragma omp parallel for reduction(+:force0,torque0) private(twomols) 
for(int j=1;j<nmol;j++) 
     { twomols.push_back(m_mols[0]); 
     twomols.push_back(m_mols[j]); 
     CMolecule::polarize_mutual(twomols,false, 1000); 
     twomols[0]->computeMol_Force_and_Torque(forcetemp,torquetemp); 
     force0+=forcetemp; 
     torque0+=torquetemp; 
     forcetemp.zero(); 
     torquetemp.zero(); 
     twomols.clear(); 
     } 
    REAL converter=COUL_K*IKbT; 
    force0*=converter; 
    torque0*=converter; 
    return; 
    } 

Quando compilo il codice, dà il seguente messaggio:

EnergyD_multi.cpp: In static member function ‘static void 
CEnergymulti::forcetwobody(std::vector<CMolecule*, 
std::allocator<CMolecule*> >, CPnt, CPnt)’: EnergyD_multi.cpp:226: 
error: ‘torque0’ has invalid type for ‘reduction’ 
EnergyD_multi.cpp:226: error: ‘force0’ has invalid type for 
‘reduction’ 

ho capito che le variabili 'force0' e 'torque0' non sono né il tipo di dati double o integer, ma di tipo 'CPnt', una classe che è definita per rappresentare vettori tridimensionali nello spazio. Per la classe 'CPnt', l'operatore '+' e '-' sono già stati definiti dall'overloading dell'operatore. Quindi le mie domande sono: è vero che la riduzione in OpenMP non può gestire tali operatori sovraccaricati? Esistono modi alternativi per parallelizzare questo ciclo con OpenMP senza ridurre i componenti di "force0" e "torque0"?

Grazie mille.

+0

Hai funzionato? Ho cambiato il tuo codice per eseguire l'operazione di sovraccarico con OpenMP. Fammi sapere se funziona. Si prega di vedere il link per capire l'idea principale nel caso ci sia un bug. –

+0

Grazie mille @raxman. Con lo schema di parallelizzazione hai suggerito che il codice potrebbe essere compilato. Tuttavia, sono ancora un po 'confuso riguardo la parallelizzazione del ciclo con 'nowait'. Questo perché credo ancora che per ogni thread le variabili nel ciclo "twomols" e "forcetemp" debbano essere considerate "private", ma non le hai elencate come private. – user2226358

+0

Tutto all'interno di "#pragma omp parallel" è privato per definizione. Solo force0 e torque0 sono condivisi perché sono al di fuori del prompma omp –

risposta

7

È vero che la riduzione di OpenMP non può gestire tali operatori sovraccaricati. Tuttavia, c'è un'alternativa. Un modo per riscrivere una riduzione in OpenMP consiste nell'utilizzare i parametri nowait e atomic. http://bisqwit.iki.fi/story/howto/openmp/#ReductionClause . Questo è veloce come il modo normale.

Se si sostituisce atomic con critical è possibile utilizzare operatori sovraccaricati più complessi. Questo non è veloce come usare atomic ma funziona ancora bene nella mia esperienza.

Ho fatto questo in modo da poter utilizzare operatori che operano su 4 o 8 galleggianti contemporaneamente (con SEE o AVX). reduction with OpenMP with SSE/AVX

Edit: ho cambiato il codice per riflettere ciò che penso sarebbe fare quello che vuoi.

void CEnergymulti::forcetwobody(vector<CMolecule*> m_mols,CPnt force0,CPnt torque0) 
{ 
    const int nmol=m_mols.size(); 
    force0.zero(); 
    torque0.zero(); 
    #pragma omp parallel 
    { 
     CPnt force0_private; 
     CPnt torque0_private; 
     force0_private.clear(); 
     torque0_private.clear(); 
     #pragma omp for nowait 
     for(int j=1;j<nmol;j++) 
     { 
      CPnt forcetemp,torquetemp; 
      forcetemp.zero(); 
      torquetemp.zero(); 
      vector<CMolecule*> twomols(2); 
      twomols.clear(); 
      twomols.push_back(m_mols[0]); 
      twomols.push_back(m_mols[j]); 
      CMolecule::polarize_mutual(twomols,false, 1000); 
      twomols[0]->computeMol_Force_and_Torque(forcetemp,torquetemp); 
      force0_private+=forcetemp; 
      torque0_private+=torquetemp; 
     } 
     #pragma omp critical 
     { 
      force0 += force0_private; 
      torque0 += torque0_private; 
     } 

    } 
    REAL converter=COUL_K*IKbT; 
    force0*=converter; 
    torque0*=converter; 
    return; 
} 
+0

Penso che il secondo pragma (cioè '#pragma omp parallelo per nowait') dovrebbe essere' #pragma omp per nowait', cioè senza il parallelo (Il costrutto parallelo è già stato impostato al di fuori di esso). Il mio compilatore si lamenta del fatto che non si può avere un nowait accanto a un parallelo per, poiché si suppone che ci sia una barriera implicita dopo il parallelo per. Sembra ora e parallelo per le direttive in conflitto. –

+0

@VarunGulshan, hai ragione, dovrebbe essere "#pragma omp per ora". L'ho appena risolto –

+0

Stai perdendo la complessità logaritmica come questa? --- Il caso peggiore è che ogni thread attende di entrare nella sezione critica e quindi stanno aggiungendo in sequenza i risultati. --- Quindi alla fine avete 'O (nmol/thread_num) + O (thread_num)' invece di 'O (nmol/thread_num) + O (log (thread_num))' --- Questo potrebbe rendere un'enorme differenza su un potente computer (es. supercomputer) – Jan