2016-07-19 43 views
0

Ich benutze C++ (geschrieben für Windows und Linux) für OpenMPI kompiliert. Ich bekomme eine seltsame Reihe von Zuordnungsfehlern, wenn ich ein Klassenobjekt zu einem Vektor innerhalb der OpenMPI for-Schleife hinzufüge. Das Debuggen zeigt ein sich änderndes Muster von Zuweisungsfehlern, alle zentriert um meine "AddEntry()" - Methode, aber die Fehler fallen niemals an einer konsistenten Stelle in der Schleife oder auf einem konsistenten Element innerhalb des Objekts, das ich hinzufüge (daher glaube ich das Objekt ist nicht das Problem, diese Details sind nicht im Fragecode enthalten). Ich habe versucht, Platz für den Vektor zu reservieren, und ich habe versucht, Lösungen sowohl mit Deque und Liste. Ich habe versucht, das Objekt an das Add-Mitglied als ein Objekt, Verweis und Zeiger übergeben (instanziiert mit 'neu()') und keine dieser Lösungen das Problem gelöst. Dies ist mein Code:C++ Alloc Fehler Hinzufügen von Objekten zu Vector (oder Deque oder List) in einem OpenMPI For-Schleife

#include "MyEntryClass.h" 
#include "MyVectorClass.h" 

#include <omp.h> 

CMyVectorClass::CMyVectorClass() 
{ 
    try 
    { 
     m_vEntries.clear(); 
     m_vEntries.reserve(750000); 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [CMyVectorClass] Exception Code: " << ex.what() << "\n"; 
    } 
} 

// Interface (public) 

bool CMyVectorClass::AddEntry(CMyVectorClass_Entry& eAdd) 
{ 
    try 
    { 
     m_vEntries.push_back(eAdd); 

     return true; 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [AddEntry] Exception Code: " << ex.what() << "\n"; 
    } 

    return false; 
} 

bool CMyVectorClass::DoOMPLoop() 
{ 
    // Max processors for omp 
    int nMaxProcs 
    // Continue, if true 
    volatile bool vbContinue = true; 
    // Loop counter 
    long lMaxCount = 100000; 

    try 
    { 
     // Iterate through files 
     // Declare team size 
     #pragma omp parallel for shared(vbContinue) num_threads(nMaxProcs) 
     for (long lCount = 0; lCount < lMaxCount; lCount++) 
     { 
      // The entry object to add 
      CMyEntryClass cAdd; 

      // Do some stuff to the entry 
      cAdd.SetStuff(); 

      // Catalog the data 
      vbContinue = AddEntry(cAdd); 
     } 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [DoOMPLoop] Exception Code: " << ex.what() << "\n"; 
    } 

    return false; 
} 

// Implementation (private) 

Dieses Problem hat mich viele Stunden der Frustration kosten versuchen zu lösen und keine der Hilfe, die ich auf Stackoverflow finden (oder dem ‚Netz im Allgemeinen) hat mir ermöglicht, das Problem zu beheben (obwohl es mir geholfen hat, anderen Code zu optimieren). Bitte helfen.

Antwort

0

Nach vielen Versuchen und Drangsalierung erkannte ich, dass die Zuteilungsfehler nicht die Ursache des Problems waren (da meine Maschine über einen Speicher verfügt und das Reservelimit nie annähernd überschritten wurde). Ich fing an, die AddEntry() - Methode innerhalb des OpenMPI zu vermuten, die Schleife verursachte Kollisionen. Also habe ich "resize" anstelle von "reserve" verwendet und ich habe eine indexierte "SetEntryAtIndex()" -Funktion verwendet, um ein Objekt an einer bestimmten Stelle im Vektor einfach zurückzusetzen (beachten Sie, dass diese Art von Direktzugriff nicht bei allen ähnlichen Containern erlaubt ist) . Dies ist mein Code jetzt:

#include "MyEntryClass.h" 
#include "MyVectorClass.h" 

#include <omp.h> 

CMyVectorClass::CMyVectorClass() 
{ 
    try 
    { 
     m_vEntries.clear(); 
     m_vEntries.resize(750000); 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [CMyVectorClass] Exception Code: " << ex.what() << "\n"; 
    } 
} 

// Interface (public) 

bool CMyVectorClass::SetEntryAtIndex(CMyVectorClass_Entry& eSet, long lIndex) 
{ 
    try 
    { 
     if ((lIndex >= 0) && (lIndex < m_vEntries.size())) 
     { 
      m_vEntries[lIndex] = eSet; 

      return true; 
     } 
     else 
     { 
      ReportTimeStamp("[SetEntryAtIndex]", "ERROR: Index [" + ConvertLongToString(lIndex) + "] is Out of Range [0:" + ConvertLongToString(m_vEntries.size()) + "]"); 
     } 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [SetEntryAtIndex] Exception Code: " << ex.what() << "\n"; 
    } 

    return false; 
} 

bool CMyVectorClass::DoOMPLoop() 
{ 
    // Max processors for omp 
    int nMaxProcs 
    // Continue, if true 
    volatile bool vbContinue = true; 
    // Loop counter 
    long lMaxCount = 100000; 

    try 
    { 
     // Iterate through files 
     // Declare team size 
     #pragma omp parallel for shared(vbContinue) num_threads(nMaxProcs) 
     for (long lCount = 0; lCount < lMaxCount; lCount++) 
     { 
      // The entry object to add 
      CMyEntryClass cAdd; 

      // Do some stuff to the entry 
      cAdd.SetStuff(); 

      // Catalog the data 
      vbContinue = SetEntryAtIndex(cAdd, lCount); 
     } 
    } 
    catch (exception ex) 
    { 
     cout << "ERROR [DoOMPLoop] Exception Code: " << ex.what() << "\n"; 
    } 

    return false; 
} 

// Implementation (private) 

Der Trick, um ein vollständig zu erstellen ist Ändern der Größe (und sogar OVER) besiedeltes Vektor und dann einen Index, um sicherzustellen, sind keine Kollisionen innerhalb der OpenMPI Schleife.

+0

Statt explizit für den Index außerhalb der Grenzen wie die Überprüfung, die alle benötigten Sie war Gebrauch 'vector :: at()' und fangen die 'std :: out_of_range' zu ​​tun Ausnahme, wenn ein Begrenzungsproblem vorliegt. 'try {m_vEntries.at (lIndex) = eSet; return true;} catch (std :: out_of_range & e) {Fehler} ' – PaulMcKenzie

+0

Dies ist nur eine weitere Lösung für das Problem. Der Punkt ist, eine Standard-Push-Back-Operation wird nicht innerhalb einer parallelen Schleife funktionieren. Die Lösung, die ich anbiete, erfüllt zwei Dinge: 1) sie vermeidet Kollisionen und 2) sie weist den für den Vektor benötigten Platz vor. Da ich bereits eine Vorstellung von der Größe der Sammlung habe (Code nicht gezeigt, gebe ich eine Liste von Elementen, um sie zu füllen), vermeidet die Verwendung der "resize (#)" - Syntax kostspielige Neuzuweisungen des Arrays (was nach meinem Verständnis vollständige Kopien des Datensatzes erfordert). Eine Ausnahme zu finden scheint eine minderwertige Lösung zu sein, da sie unnötig ist. – Epsilon3

-1

Ich denke, es muss etwas anderes falsch mit dem ersten Ausschnitt sein. Vielleicht verdirbst du den Vektor im Code, der nicht gezeigt wird? Der kleine Ausschnitt unten funktioniert mehr oder weniger gleich und funktioniert gut. Beachten Sie, dass die Schleife über das Ende hinausgeht und der Vektor sich selbst ändert.

#include <vector> 

int main(int arg,char*arv[]) 
{ 

    std::vector <int> my_vec; 

    my_vec.clear(); 
    my_vec.resize(750000); 

    for(int i=0;i<760000;i++) 
    my_vec.push_back(i); 
    return 0; 
} 

--Matt

+0

Dieser Code/Vorschlag behandelt das Problem nicht. Das Problem ist Vektor (oder Deque oder Liste) push_back in OpenMPI. Ihr Code ist OHNE OpenMPI in Ordnung. Die Kollisionen treten auf, wenn die for-Schleife in eine OpenMPI-Anweisung eingebettet ist (beachten Sie die Anweisung #pragma omp ... im Codebeispiel). Es ist der Multiprozessor push_back, der die Kollisionen verursacht - tatsächlich war es ein schwerer Fehler, den es zu fangen gab, weil er manchmal am Anfang, am Ende, an einem String-Feature-Set, an einem langen Feature-Set auftauchte ohne offensichtlichen Misserfolg. – Epsilon3

+0

Ich verstehe. Ja, der STL-Vektor ist für Multithread-Schreibzugriff nicht sicher. Ihre zweite Lösung betrifft mich immer noch und beschäftigt sich wahrscheinlich mit undefiniertem Verhalten. Zum Beispiel kann die Kollision immer noch auftreten, wenn der Vektor wächst.Was passiert, wenn zwei Threads gleichzeitig in denselben Index schreiben und das Element komplex ist? Im besten Fall wird es eine Race-Bedingung schlimmstenfalls wird es das Element verfälschen Braucht Ihre Lösung - mehr als ein Thread in den Vector gleichzeitig schreiben? Oder können Sie beim Schreiben einen Mutex einführen? Ein Mutex löst die Nebenläufigkeitsprobleme. –

+0

Die Verwendung des Index COMBINED mit der anfänglichen Größenanpassung (#) vermeidet beide Probleme. Die resize() -Syntax (im Gegensatz zu reserve()) füllt den Vektor mit Objekten. Da die OpenMP-Schleife explizit die Deklaration und vollständige Verwaltung des Zählers in seinem Zuständigkeitsbereich erfordert, gibt es immer nur einen Index. Ein Index. Ein Artikel pro Index. Keine Notwendigkeit, die Größe erneut zu ändern (Code nicht gezeigt, ich übergebe eine Liste von Objekten, um den Vektor zu füllen, also kenne ich seine Größe). Mutexes sind eine Lösung, aber eine kompliziertere (hier verwende ich nur den Zusatz eines Index, der der Zähler ist, keine andere Objektverwaltung erforderlich.) – Epsilon3