Speicherverwaltung schreiben: Problem bei assert und free.



  • Folgende Änderungen haben zu einer Lauffähigkeit geführt.

    m_numOfFreeChunks++;
    m_freeChunks[m_numOfFreeChunks] = offset / m_bucketGranularity;
    
    =>
    
    m_freeChunks[m_numOfFreeChunks++] = offset / (long)m_bucketGranularity;
    
    ---
    
    long offset = m_basePointer - pointer;
    
    =>
    
    long offset = ((unsigned char*)pointer - m_basePointer);
    
    ---
    
    for (int i = capacity - 1; i >= 0; --i) {
        m_freeChunks[i] = capacity - i;
    }
    
    =>
    
    for (unsigned short i = 0; i < m_numOfFreeChunks; ++i) {
    	m_freeChunks[i] = i;
    }
    
    ---
    
    Bucket b = *m_buckets[i];
    v = b.RequestMemory(capacity);
    
    =>
    
    v = m_buckets[i]->RequestMemory(capacity);
    
    ---
    
    Bucket b = *m_buckets[i];
    if (b.ReleaseMemory(pointer)) {
    
    =>
    
    if (m_buckets[i]->ReleaseMemory(pointer)) {
    

    Ich hoffe ich habe keine Änderungen vergessen.

    Ich habe leider nicht alle diese Änderungen verstanden, warum es vorher ein Problem war. Vorallem das mit dem . und dem ->. Da habe ich noch Probleme mit den * und & Operatoren. Möglicherweise kann mir das nochmal jemand veranschaulichen, warum ich hier nicht mit . arbeiten darf und wie * und & da reinspielen.

    Danke - Enomine



  • Dies ist lauffähig:

    BucketAdmin::BucketAdmin(void)
    {
    	// PLACE YOU IMPLEMENTATION HERE.
    	unsigned short capacity = BUCKET_CAPACITY;
    	for (int i = 0; i < NUM_OF_BUCKETS; i++) {
    		unsigned short granularity = m_bucketSizes[i];
    		m_baseMemory = (unsigned char*)malloc(sizeof(Bucket));
    		Bucket *b = new(m_baseMemory) Bucket(granularity, capacity);
    		m_buckets[i] = b;
    	}
    }
    

    Jedoch stellt sich mir die Frage ob da in Verbindung mit dem Destruktor (free(m_baseMemory)) ein Speicherleck entsteht dadurch, dass nur das letzte m_baseMemory noch bekannt ist und die davor nicht mehr. Ist es so besser?

    BucketAdmin::BucketAdmin(void)
    {
    	// PLACE YOU IMPLEMENTATION HERE.
    	unsigned short capacity = BUCKET_CAPACITY;
    	m_baseMemory = (unsigned char*)malloc(sizeof(Bucket)*NUM_OF_BUCKETS);
    	Bucket* newBucketP = (Bucket *)m_baseMemory;
    	for (int i = 0; i < NUM_OF_BUCKETS; i++) {
    		unsigned short granularity = m_bucketSizes[i];
    		Bucket *b = new(&newBucketP[i]) Bucket(granularity, capacity);
    		m_buckets[i] = b;
    	}
    }
    

    Danke - Enomine



  • Schlangenmensch schrieb:

    Wenn Ihr Programm fehlerfrei und ohne assertations durchläuft haben Sie Ihre erste Speicherverwaltung geschrieben. Wenn Sie jetzt in der Datei BucketSystem in den beiden new Routinen jeweils die erste Zeile auskommentieren und die zweite Zeile einkommentieren, verwenden Sie für alle Speicheranforderungen wieder das Standard malloc. Auf meinem Rechner ist die Variante mit dem eigenen Bucket Memory Manager gut doppelt so schnell.

    Kann das hier jemand verifizieren? Für mich klingt es erstmal Zweifelhaft, dass ein eigener "new" operator eine Performance Vorteil von 100% bringt. Sonst muss ich da am Wochenende mal was rumprobieren.

    Hey hab dir das aktuelle, laufende Projekt mal nochmal hochgeladen.
    http://www.share-online.biz/dl/GXKAM43PFJQ

    Habe mit einkommentierter erster Zeile 7 Sekunden.
    Habe mit einkommentierter zweiter Zeile 10 Sekunden.
    Also hier pie mal Daumen 30% Geschwindigkeitsverbesserung.

    Danke - Enomine



  • Hi, ich habe mal versucht ein paar Beispiele für *, &, -> usw. zu erstellen. Ich hoffe, ich habe nichts vergessen

    #include <iostream>
    class A
    {
    public:
       void doThis() { std::cout << "number of calls: " << a++ <<"\n";};
    
       int a = 0;
    };
    
    int main()
    {
       A* pointerToA = new A; //dynamisch erstellt. Pointer auf ein Objekt vom Typ A
       pointerToA->doThis();  // da Pointer, Aufruf mit ->
    
       (*pointerToA).doThis(); //Erst derefenziert, dann aufruf mit "." da dereferenziert.
    
       A copyOfderefencedPointer = *pointerToA; //Pointer wird dereferenziert und anschließend eine Kopie vom Objekt erstellt.
       copyOfderefencedPointer.doThis();
    
       delete pointerToA; //Obkelt auf das der Pointer Zeigt wird zerstört. Oben angelegte Kopie existiert weiter.
    
       A a; //Das, wie man es in C++ eigentlich immer haben will
       a.doThis();
    
       A* otherPointer = &a;   //Nur so lange gültig, wie a existiert
       otherPointer->doThis(); //Gleiches Ergebnis wie a.doThis();
    
       A& referenceToA = a;    //Referenz auf a. Alle Änderungen an referenceToA werden an a vorgenommen. im Ergebnis, dass selbe, wie oben mit otherPointer. Nur, dass refenceToA natürlich kein nullptr werden kann.
       referenceToA.doThis();
    }
    

    Was deine Zeitmessung betrifft, wie hast du denn gemessen und vor allem, mit welchen Optimierungsflags hast du kompiliert?

    Nachdem ich mir den Code gestern genauer angeschaut habe, kann ich mir gut vorstellen, dass er schneller läuft, als normaler dynamischer Speicher. Du erstellst halt ein Memory Pool, der am Anfang einmal alloziert wird. Um fair zu sein, müsstest du die Allozierung mit messen.



  • Hey Schlangenmensch,

    wenn ich in dem Projekt das Programm starte dann steht dort "Lokaler Windows Debugger" auf dem Button. In diesem Modus wird dann bei der Ausführung in Visual Studio eine Sekundenanzeige angezeigt im rechten Bereich. Mit der Speicherverwaltung endet das Programm pie mal Daumen bei 7 Sekunden (eine 6 oder 8 habe ich nie gesehen), ohne Speicherverwaltung bei 10 Sekunden (9 und 11 nie gesehen).

    Danke - Enomine



  • Geschwindigkeitsmessungen sind nur sinnvoll, wenn das Programm
    a) optimiert wurde (Release in VS)
    b) nicht im Debugger ausgeführt wird



  • Hi Enomine,

    Visual Studio kann verschiedene Konfigurationen für einzelne Projektemappen anlegen. Du hast oben irgendwo ein Drop Down Menü, bei dem du die aktive Konfiguration auswählen kannst. Wahrscheinlich steht bei dir da "Debug". Mit der Konfiguration, kannst du im Debugger durch die einzelnen Zeilen deines Programms durch gehen und dir überall anschauen, was für Werte in deinen Variablen steht.
    Die für die Ausführung interessantere Konfiguration ist "Release". Dort setzt Visual Studio Optimierungsflags. Wie genau und ob optimiert wird, kannst du in den Projekteinstellungen einstellen.
    Wenn du dein Projekt im Release Modus erstellst, wird das Ergebnis deutlich schneller sein. Du kannst das Ergebnis auch so ausführen und musst das nicht über den Debugger machen (einfach die exe starten).
    Auch Visual Studio kann dein Program ohne Debugger ausführen (Ctrl + F5).


Anmelden zum Antworten