2009-04-27 4 views
5

Ich habe vor kurzem tiefer in C++ gegangen und meine Bugs scheinen komplex zu werden.C++: Wie ist es möglich, dass das Lesen von Daten den Speicher beeinträchtigen kann?

Ich habe einen Vektor von Objekten, jedes Objekt enthält einen Vektor von Schwimmern. Ich entschied, dass ich ein weiteres flaches Array erstellen musste, das alle Float-Werte aller Objekte in einem enthielt. Es ist ein wenig komplexer als das, aber der Kern des Problems ist, dass, während ich meine Objekte durchläuft, die die Float-Werte extrahieren, irgendwann wird mein Vektor von Objekten verändert oder auf eine seltsame Art und Weise beschädigt. (Meine Leseoperationen sind alle const Funktionen)

Ein anderes Beispiel war mit MPI. Ich wollte gerade anfangen, also wollte ich genau denselben Code auf zwei verschiedenen Knoten mit eigenem Speicher laufen lassen, ohne dass Daten übertragen wurden, alles sehr einfach. Zu meiner Überraschung habe ich Segmentierungsfehler und Nach-Stunden-Tracking, ich fand, dass eine Zuweisung einer Variablen eine völlig andere Variable auf NULL gesetzt.

So bin ich neugierig, wie ist es möglich, dass Lesevorgänge meine Datenstrukturen beeinflussen können. Ähnlich kann sich eine scheinbar nicht zusammenhängende Operation auf eine andere auswirken. Ich könnte keine Lösungen für meine Probleme mit diesen kurzen Beschreibungen erwarten, aber jeder Rat wird sehr geschätzt werden.

Update: Hier ist ein Teil des Codes, den ich ursprünglich nicht posten, weil ich nicht sicher bin, wie viel davon extrahiert werden kann, ohne das ganze System zu verstehen.

Eine Sache, die ich gerade herausgefunden habe, war, dass, als ich aufhörte, den Wert zu meinem flachen Array zuzuweisen und stattdessen nur cout'ed, die Seg-Fehler verschwanden. Vielleicht deklariere ich mein Array falsch, aber selbst wenn ich es war, bin ich mir nicht sicher, wie es den Objektvektor beeinflussen würde.

void xlMasterSlaveGpuEA::FillFlatGenes() { 
    int stringLength = pop->GetGenome(0).GetLength(); 
    for (int i=0;i<pop->GetPopSize();i++) 
     for (int j=0;j<stringLength;j++) 
      flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j); 
} 

float xlVectorGenome::GetFloatGene(unsigned int i) const { 
    return GetGene(i); 
} 

meine Wohnung Array ist eine Memberfunktion

float * flatFitness; 

im Konstruktor initailsed wie so:

flatFitness = new float(popSize); 

Update 2:

Ich möchte nur darauf hinweisen, dass die Zwei Beispiele oben sind nicht verwandt, die erste ist nicht multi-threaded. Das zweite MPI-Beispiel ist technisch, aber MPI ist verteilter Speicher, und ich habe bewusst die einfachste Implementierung versucht, die mir einfällt, nämlich beide Maschinen, die Code unabhängig voneinander ausführen. Es gibt jedoch eine zusätzliche Detail, habe ich in einem condtional sagen

if node 1 then do bottom half of loop 

if node 1 then do top half 

Auch der Speicher isoliert werden sollten, sollten sie arbeiten, als ob sie nichts voneinander wissen .. aber diese Bedingung zu entfernen und machen beide Schleifen zu tun alle Würfel, beseitigt den Fehler

+0

Können Sie eine verkürzte Version Ihres Codes veröffentlichen? Es ist sehr schwer zu sagen, was genau passieren könnte. – tgamblin

+0

Könnten Sie den Schleifencode, wo Sie die Objekte im Vektor iterieren, posten ... möglicherweise treten Sie dort auf Speicher? – Balk

+1

Lesevorgänge ändern Werte nicht. Es muss noch etwas in Ihrem Code sein. Ihr zweites Beispiel ist eine typische Situation einer Variablen, die auf den falschen Ort zeigt. – fbinder

Antwort

14

Dies ist kein Array-Konstruktor:

Sie erstellen hier einen Float auf dem Heap, initialisiert mit dem Wert popSize. Wenn Sie ein Array von Schwimmern wollen, müssen Sie Klammern statt Klammern verwenden:

float *flatFitness = new float[popSize]; 

Diese leicht, die Probleme verursachen könnte Sie beschreiben. Denken Sie auch daran, wenn Sie Arrays erstellen, müssen Sie mit delete [] (eventuell) löschen:

delete [] flatFitness; 

Wenn Sie nur delete verwenden, es könnte funktionieren, aber das Verhalten ist nicht definiert.

Wenn Sie vermeiden möchten, die Array-Syntax insgesamt zu verwenden, warum nicht std::vector verwenden? Sie können einen Vektor von popSize Elemente wie folgt erstellen:

#include <vector> 

std::vector<float> flatFitness(popSize); 

Diese automatisch freigegeben wird, wenn es außerhalb des Bereichs fällt, so dass Sie sich keine Gedanken über new oder delete zu kümmern.

Update (re: Kommentar): Wenn Sie bereits std::vectors an anderer Stelle in Ihrem Code verwenden, werfen Sie einen Blick auf std::vector::swap(). Sie können möglicherweise vermeiden, Dinge komplett zu kopieren, und einfach ein paar Vektoren zwischen der Pufferung für CUDA und der Verarbeitung, die Sie hier tun, hin- und herwechseln.

+0

danke, ich werde das überprüfen – zenna

+0

komischerweise Ich extrahiere die Daten von Std :: Vektoren wie ich es über CUDA an die GPU senden muss. Es gibt wahrscheinlich einen viel eleganteren Weg – zenna

+0

Werfen Sie einen Blick auf vector :: swap(). Möglicherweise können Sie hier einige Vektoren instanziieren und sie dann mit denen, die Sie für CUDA verwenden, austauschen, sodass Sie überhaupt keine Kopien erstellen müssen. Stellen Sie sicher, dass sie die richtige Größe haben, indem Sie resize() oder den Konstruktor verwenden (wie oben), oder dass Sie in den Speicher schreiben, den Sie nicht haben. – tgamblin

0

Ich vermute, dass Sie Multi-Threading oder Speicherbeschädigung Probleme haben, die Sie nicht bewusst sind. Das Verhalten, das Sie beschreiben, ist keine Art von Standard, von Design, wünschenswertem Verhalten.

+0

Ja, das klingt wie ein fast Lehrbuch Fall von fehlenden Speicherbarrieren und unzureichende Inter-Thread-Synchronisation. Es dauert nur einen Thread, um das Datenobjekt zu einem Zeitpunkt zu aktualisieren, nachdem es für einen anderen Thread sichtbar wird, und ein Mangel an Synchronisation wird Sie früher oder später beißen. –

+1

Vielleicht, außer dass er nie gesagt hat, dass er Threads benutzt. MPI ist Parallelisierung auf Prozessebene, es sei denn, Sie kombinieren es mit etwas anderem. – tgamblin

+0

Heh, als ich geantwortet habe, wurde kein Code geschrieben. –

-1

Jeffamaphone kann richtig sein, dass dies ein Threading-Problem ist. Eine andere Möglichkeit ist, dass die Objekte, die Sie gerade lesen, bereits gelöscht wurden. Sie würden dann von einer ungültigen Adresse lesen. Es ist auch möglich, dass die Datenstrukturen, in die Sie zu diesem Zeitpunkt schreiben, am selben Ort gespeichert werden, an dem die Vektoren früher waren. Dies würde zu dem Verhalten führen, das Sie beschreiben.

EDIT (basierend auf dem Update):

Dieses fehlerhaft sein kann: stringLength außerhalb der äußeren Schleife initialisiert, aber es sieht aus wie es während dieser äußeren Schleife aktualisiert werden muss:

int stringLength = pop->GetGenome(0).GetLength(); 
for (int i=0;i<pop->GetPopSize();i++) 
    for (int j=0;j<stringLength;j++) 
     flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j); 

Empfohlene fix:

for (int i=0;i<pop->GetPopSize();i++) { 
    int stringLength = pop->GetGenome(i).GetLength(); 
    for (int j=0;j<stringLength;j++) { 
     flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j); 
    } 
} 
Verwandte Themen