2010-06-24 16 views
5

Hei Community,Zeit zum Löschen von Zeigern

Ich habe eine kleine Frage bezüglich der Löschung von Zeigern.

Ich arbeite mit Zeiger-zu-Zeiger-Matrizen der Dimension 1024x1024. Da ich sie dynamisch erstelle, lösche ich den zugewiesenen Speicherplatz für sie am Ende des Programms. Aber dies in der üblichen Schleife zu tun kostet ziemlich viel Zeit - ich habe etwa 2 Sekunden mit der Taktrate des Prozessors gemessen. Und 2 Sekunden ist RIESIG, wenn das Programm nur 15 Sekunden läuft - plus: die Funktion, die diese zugewiesenen Zeiger verwendet, wird mehr als nur einmal aufgerufen.

Hier ist das gemessene zeitkritischen Stück Code einschließlich der Messung:

time=clock(); 
for(i=0;i<xSize;i++){   //xSize is dynamic, but 1024 for the measurement 
    delete [] inDaten[i]; 
    delete [] inDaten2[i]; 
    delete [] copy[i]; 
} 
delete inDaten; delete inDaten2; delete copy; 
time=clock()-time; 
    time/=CLOCKS_PER_SEC; 

Ist Zeiger immer so lange zu löschen? Oder mache ich nur Dinge falsch?

Ich hoffe, dass jemand hier mir helfen kann. Da ich ein ziemlich komplexes Programm optimiere, um schneller zu laufen, kann ich diese 2sec-Stück Code nicht verwenden. Es ist einfach zu langsam im Vergleich zu allen anderen Teilen. Trotzdem muss ich diesen Code dynamisch implementieren können. SmartPointers könnten hilfreich sein, aber wenn ich richtig verstehe, brauchen sie auch die Zeit, sich selbst zu löschen - nur zu einer anderen Zeit ...

Vielen Dank für Ihre Antworten!

Baradrist

EDIT: Ich fand heraus, nur, dass diese Lösch-Berechnungen Messung ziemlich langsam ist, weil ich es nicht in Release-Modus kompiliert hat. Da der Debugger ins Spiel kommt, habe ich diese (letztlich unrealistischen) Zahlen gemessen, die mir Kopfschmerzen bereiteten. Das endgültige Programm wird automatisch so weit optimiert, dass der Löschvorgang nahezu entfällt.

Wie auch immer: Danke für die hilfreichen Antworten! Sie haben mir eine Menge Zusatzwissen und Dinge zum Nachdenken gebracht !!!!

+1

Was ist der Typ der Objekte in 'inDaten',' inDaten2' und 'kopieren'? Sind das nur Intarsien oder sowas, oder sind sie echte Objekte? –

+1

Müssen Sie alles dynamisch zuweisen? Brauchen Sie so viele * separate * Zuweisungen? Ist es möglich, das Programm neu zu schreiben, um die Daten in weniger, größeren Zuordnungen zu speichern? – jalf

+0

Auf meinem Netbook geht das Löschen von double [] nicht zum nächsten Takt, also entweder mit einem angehängten Debugger oder etwas teurem passiert in den Destruktoren der Elemente im Array. –

Antwort

3

delete[] wird auch Destruktoren für jedes Element des Arrays aufrufen, das Zeit hinzufügt, es sei denn, der Destruktor ist trivial.

Ansonsten - ja, dynamische Speicherzuweisung ist relativ teuer. Wenn Sie es nicht tolerieren können - versuchen Sie weniger Blöcke größerer Größe zuzuordnen oder gehen Sie ohne dynamische Zuordnung in zeitkritischen Dingen aus.

Intelligente Zeiger helfen nicht viel - sie machen die gleiche Freigabe innerhalb. Sie sind nicht für Beschleunigung, aber für Design Bequemlichkeit.

1

Wenn Sie sie am Ende des Programms löschen und es keine Rolle spielt, ob Destruktoren ausgeführt werden, lassen Sie einfach die Löschung aus - der Speicher wird vom Betriebssystem freigegeben. Versuchen Sie andernfalls, einzelne Indirektionen zu verwenden, d. H. Keine Pointer-Pointer-Arrays. Abgesehen von der Verringerung der Löschzeit verbessert dies auch die Fundstelle.

+2

Es kann nicht garantiert werden, dass das Betriebssystem überhaupt Speicher freigibt. – Yacoby

+3

@Yacoby: Es sei denn, das Betriebssystem selbst gibt eine solche Garantie. Was jedes moderne (Desktop-) Betriebssystem tut. – jalf

+0

Unabhängig davon, ob das Betriebssystem den Prozessspeicher beim Beenden bereinigt oder nicht, ist es immer noch eine schlechte Übung, sich auf eine solche Krücke zu verlassen. Besser ist es, einen Allokator zu verwenden, der für eine schnelle Freigabe besser geeignet ist, und/oder den Code neu zu gestalten, um die Freigabe zu minimieren (ohne natürlich Lecks einzuführen). – Void

2

Hier ist ein interessanter Thread „Memory Allocation/Deallocation Bottleneck?

Zuweisung und Freigabe eine lange Zeit in Anspruch nehmen und damit sind eine der häufigsten teure Operationen, die Sie haben. Dies liegt daran, dass die Heap-Verwaltung einige Dinge erledigen muss. Üblicherweise werden die Speicherblöcke im Debug-Modus noch mehr überprüft. Wenn du die gleiche Zeit in Release-Konfiguration hast würde ich überrascht sein, normalerweise gibt es einen Faktor zwischen mindestens 2. Mit einem privaten Heap kannst du die Dinge dramatisch erhöhen.Wenn Sie immer Objekte der gleichen Größe zuweisen, dann könnte ein Speicherpool die beste Alternative sein.

0

Versuchen Sie, eigene Speicherzuweisungsmethoden zu erstellen, um die Zerstörungszeit zu reduzieren.

Fordern Sie zum Beispiel einen Speicherblock von Os an und weisen Sie ihm das Array zu, damit Sie den gesamten Block in einem einzigen Arbeitsschritt freigeben können.

1

Es sieht aus wie das Problem in der Struktur Ihrer Daten ist. Warum brauchen Sie so viele dynamische Zuordnungen? Was kann getan werden, um die Anzahl der Zuweisungen zu reduzieren?

Wenn das Freigeben der Zeiger 2 Sekunden dauert, dauert es wahrscheinlich mindestens so viel, sie zuzuweisen.

Die Befreiung von ihnen kann vermieden werden, indem Sie das Programm vorzeitig beenden. C++ gibt keine Garantie darüber, was mit dem zugewiesenen Speicher dann passieren wird, aber Ihr Betriebssystem wahrscheinlich, also in praktischen Bedingungen, könnte es eine einfache Möglichkeit sein, 2 Sekunden von der Ausführungszeit zu rasieren.

Aber das bleibt immer noch die> 2 Sekunden für die Zuordnungen.

Ihre beste Wette ist es, zu versuchen, die Daten besser zu strukturieren, denke ich. Können Sie uns zeigen, wie die Matrix derzeit strukturiert ist?

+0

Die Verwendung von 1-Dim-Matrizen spart Zeit! Sie haben Recht bezüglich der Struktur. Die Matrizen enthalten Graustufen von pinctures (also 1024x1024 usw.). Aber immer noch: die Zuordnung ist viel schneller als die Freigabe (gemessen) – Baradrist

0

Vielen Dank für alle schnellen Antworten !!! Es ist schön zu sehen, dass da draußen jemand ist, der hilft =). Für mein Problem scheint es, als müsste ich mit diesem Zeitverlust umgehen, da ich die dynamischen Arrays als temporäre Matrizen in kleineren Unterprogrammen brauche, die am Ende nicht ausgeführt werden.

Wie auch immer: wieder DANKE !! Und noch einen schönen Tag!

Baradrist

+0

Die Verwendung von eindimensionalen Matrizen sollte den Trick tun - danke! – Baradrist

0

Wenn die Objekte in Ihrem Arrays nicht-triviale Destruktoren darauf gibt es nicht viel Sie deutlich Laufzeit tun kann, dass zunächst ohne Adressierung zu verbessern. Ansonsten:

Statt machen inDaten, inDaten2 and copy Arrays der Größe isize von Zeigern auf Arrays der Größe isize warum sie nicht Arrays der Größe machen isize*isize und statt Adressierung einzelner Artikel mit: array[i][j] Adresse, um sie mit array[i*isize+j]. So können Sie mit einem Anruf auf delete [] aufräumen.

1

Sollte dies nicht sein:

delete [] inDaten; delete [] inDaten2; delete [] copy; 

denn wie verwendet, sie sind offensichtlich Arrays. (Zumindest scheinen sie auch zu sein, Sie haben nicht genug Kontext zur Verfügung gestellt).

+0

Ja, sollte es sein - schon geändert, aber es ändert nicht die Zeit-Nummern =). – Baradrist

0

Eine Optimierung dafür ist, Speicher in Chunks zu reservieren, einzelne Zeiger mit neuer Platzierung zuzuordnen und beim Löschen einfach den gesamten Chunk zu löschen.

Sie müssen jedoch vorsichtig sein, da diese Option den Destruktor für jedes Objekt, das mit Placement neu zugewiesen wird, nicht aufruft.

1

Sie sagen nicht, wie groß die Objekte in den Arrays sind, aber wenn sie ausreichend groß sind, ist es möglich, dass ein Teil des Speichers ausgelagert wurde und wieder eingetauscht werden musste Prozessraum), und das verursacht die Zeit, die Sie sehen.

0

Wenn Sie festgestellt haben, dass Speicherzuweisungen/-zuordnungen der Engpass sind und dies schneller sein soll, ist die erste naheliegende Lösung, einen zusammenhängenden Puffer für Ihre Arrays zu verwenden. Sie können weiterhin eine Matrixschnittstelle bereitstellen, die auf sie wie zweidimensionale Arrays zugreift.

// Rudimentary Implementation 
template <class T> 
class SquareMatrix 
{ 
public: 
    explicit SquareMatrix(int i_size): 
     size(i_size), mat(new T[i_size * i_size]) {} 

    ~SquareMatrix() 
    { 
     delete[] mat; 
    } 

    // could also be column depending on row-major/col-major 
    T* operator[](unsigned int row) 
    { 
     return mat + row * size; 
    } 

    // could also be column depending on row-major/col-major 
    const T* operator[](unsigned int row) const 
    { 
     return mat + row * size; 
    } 

private: 
    unsigned int size; 
    T* mat; 
}; 

Die zweite offensichtliche Sache zu tun ist, statt drei Matrizen haben eine Matrix aus einer Struktur besteht, die die Daten alle notwendigen hat. Dies setzt voraus, dass eine Matrix von Tupeln ausreicht, was bei dem von Ihnen geposteten Code der Fall zu sein scheint.

Wenn Sie wirklich hardcore gehen und mehrere Matrizen benötigen, dann schreiben Sie Ihren eigenen Speicherzuordner dafür. Sie können Speicher für mehrere Matrizen auf einmal zuweisen und sie einfach mit der neuen Platzierung erstellen. Ein wenig weiterlesen und studieren ist erforderlich, wenn Sie dies tun möchten, da das Schreiben von Speicherzuordnern keine triviale Aufgabe ist: Sie müssen Probleme wie die Ausrichtung berücksichtigen, aber das ist der schnellste Weg.

Ich empfehle, dass Sie immer noch einen Profiler greifen, anstatt sich auf Timing-Tests zu verlassen und eine ordnungsgemäße Analyse des Call Graphs Ihres Codes durchzuführen. So erfahren Sie genau, wie viel Zeit wo verbracht wird. Es könnte sein, dass die Konstruktion/Zerstörung der Objekte in Ihren Matrizen nicht so billig ist, wie es zum Beispiel sein könnte.

Abgesehen von offensichtlichen algorithmischen Ineffizienzen, sind selbst äußerst kenntnisreiche Programmierer oft unkorrekt bezüglich der Engpässe in ihrem Code. Der Profiler ist Ihr bester Freund, wenn Effizienz ein wichtiges Anliegen ist.

0

Wenn alle von den Zeigern in Ihrer Matrix referenzierten Objekte vom selben Typ sind (oder mindestens die gleiche Größe haben), können Sie einen großen Teil des Speichers zuweisen, um alle zu speichern und sie direkt zu initialisieren.