2015-08-26 4 views
9

OK, also, ich schrieb einen Code, um zu überprüfen, wie viel Speicher zur Laufzeit verfügbar ist. Eine ganze (minimale) CPP-Datei ist darunter.neu und löschen [] sind schlechter als malloc und frei? (C++/VS2012)

HINWEIS: Der Code ist nicht perfekt und nicht Best Practice, aber ich hoffe, dass Sie sich auf das Speichermanagement und nicht auf den Code konzentrieren können.

Was sie tut (Teil I):

  • (1) Ordnen Sie so viel Speicher wie möglich in einem Block. Löschen Sie diesen Speicher
  • (2) Reservieren Sie so viele mittelgroße Blöcke (16MB) wie möglich. Löschen Sie diese Erinnerung.

-> Dies funktioniert

Was sie tut (Teil II):

  • (1) in einem Block so viel Speicher wie möglich zuzuteilen. Löschen Sie diesen Speicher
  • (2) Zuordnen Sie so viele winzige Blöcke (16 KB) wie möglich. Löschen Sie diese Erinnerung.

-> Dies verhält sich komisch!

Das Problem ist: Wenn ich das wiederhole, kann ich nur 522kb für die secons vergeben weiterlaufen --->?

Es passiert nicht, wenn die zugewiesenen Blöcke z. 16 MB Größe.

Haben Sie irgendwelche Ideen, warum das passiert?

// AvailableMemoryTest.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

#include <vector> 
#include <list> 
#include <limits.h> 
#include <iostream> 


int _tmain(int argc, _TCHAR* argv[]) 
{ 


    auto determineMaxAvailableMemoryBlock = [](void) -> int 
    { 
     int nBytes = std::numeric_limits<int>::max(); 

     while (true) 
     { 
      try 
      { 
       std::vector<char>vec(nBytes); 
       break; 
      } 
      catch (std::exception& ex) 
      { 
       nBytes = static_cast<int>(nBytes * 0.99); 
      } 
     } 
     return nBytes; 
    }; 

    auto determineMaxAvailableMemoryFragmented = [](int nBlockSize) -> int 
    { 

     int nBytes = 0; 

     std::list< std::vector<char> > listBlocks; 

     while (true) 
     { 
      try 
      { 
       listBlocks.push_back(std::vector<char>(nBlockSize)); 
       nBytes += nBlockSize; 
      } 
      catch (std::exception& ex) 
      { 
       break; 
      } 
     } 
     return nBytes; 
    }; 


    std::cout << "Test with large memory blocks (16MB):\n"; 
    for (int k = 0; k < 5; k++) 
    { 
     std::cout << "run #" << k << " max mem block   = " << determineMaxAvailableMemoryBlock()/1024.0/1024.0 << "MB\n"; 
     std::cout << "run #" << k << " frag mem blocks of 16MB = " << determineMaxAvailableMemoryFragmented(16*1024*1024)/1024.0/1024.0 << "MB\n"; 
     std::cout << "\n"; 
    } // for_k 


    std::cout << "Test with small memory blocks (16k):\n"; 
    for (int k = 0; k < 5; k++) 
    { 
     std::cout << "run #" << k << " max mem block   = " << determineMaxAvailableMemoryBlock()/1024.0/1024.0 << "MB\n"; 
     std::cout << "run #" << k << " frag mem blocks of 16k = " << determineMaxAvailableMemoryFragmented(16*1024)/1024.0/1024.0 << "MB\n"; 
     std::cout << "\n"; 
    } // for_k 

    std::cin.get(); 


    return 0; 
} 

OUTPUT mit großen Speicherblöcke (das funktioniert gut)

Test with large memory blocks (16MB): 
run #0 max mem block   = 1023.67MB  OK 
run #0 frag mem blocks of 16MB = 1952MB  OK 

run #1 max mem block   = 1023.67MB  OK 
run #1 frag mem blocks of 16MB = 1952MB  OK 

run #2 max mem block   = 1023.67MB  OK 
run #2 frag mem blocks of 16MB = 1952MB  OK 

run #3 max mem block   = 1023.67MB  OK 
run #3 frag mem blocks of 16MB = 1952MB  OK 

run #4 max mem block   = 1023.67MB  OK 
run #4 frag mem blocks of 16MB = 1952MB  OK 

OUTPUT mit kleinen Speicherblöcke (Speicherzuweisung ist aus dem zweiten Lauf ab seltsam)

Test with small memory blocks (16k): 
run #0 max mem block   = 1023.67MB  OK 
run #0 frag mem blocks of 16k = 1991.06MB  OK 

run #1 max mem block   = 0.493021MB ??? 
run #1 frag mem blocks of 16k = 1991.34MB  OK 

run #2 max mem block   = 0.493021MB ??? 
run #2 frag mem blocks of 16k = 1991.33MB  OK 

run #3 max mem block   = 0.493021MB ??? 
run #3 frag mem blocks of 16k = 1991.33MB  OK 

run #4 max mem block   = 0.493021MB ??? 
run #4 frag mem blocks of 16k = 1991.33MB  OK 

UPDATE:

Dies passiert auch mit Neu und lösche [] anstelle der internen Speicherzuweisung von STL.

UPDATE:

Es funktioniert für 64-Bit (I begrenzt den Speicher, der beide Funktionen erlaubt sind 12GB zuzuteilen). Sehr seltsam. Hier ist ein Bild dieser RAM-Nutzung Version:

RAM usage

UPDATE: Es arbeitet mit malloc und frei, aber nicht mit neuen und löschen [] (oder STL wie oben beschrieben)

+1

Eine Möglichkeit: Heap-Manager neigen dazu, verschiedene Abschnitte des Heaps für verschiedene Blockgrößen zu haben. Es kann sein, dass wenn Sie zuerst alles mit kleinen Blöcken füllen, einer der kleinen Block-Heap-Bereiche alles übernimmt, wie es für zukünftige Allokationen übrig bleibt. –

+0

OK, aber warum speichert es den Speicher, anstatt ihm die nächste Zuweisung zu geben? Es wird nicht mehr verwendet ... –

+1

Warum erstellen Sie 'std :: vector ' anstelle der direkteren Methoden ('new char []' oder 'malloc')? – sfstewman

Antwort

1

Ich nehme an, dass Sie aus irgendeinem Grund 32-Bit-Code ausführen?

Beste Schätzung: Ihre malloc Implementierung verlässt Buchhaltungsdaten überall im Speicher verstreut nach dem Freigeben, wenn die Zuweisungen klein waren. Malloc-Implementierungen verwenden in der Regel unterschiedliche Zuweisungsstrategien für kleine und große Zuweisungen, sodass es nicht unangemessen ist, dass sich die Buchhaltungsinformationen an verschiedenen Stellen befinden.

Unter Unix werden kleine Zuweisungen normalerweise mit brk durchgeführt, während große Zuordnungen mit mmap(MAP_ANONYMOUS) gemacht werden. _tmain und stdafx.h bedeutet, dass Sie auf Windows testen, also IDK darüber, wie dieses malloc unter der Haube funktioniert.

+0

Ihr erster Satz ist seltsam. Sie scheinen zu implizieren, dass es irgendwie ungewöhnlich oder seltsam ist, 32-Bit-Code auszuführen. Kannst du das erklären? –

+1

@LightnessRacesinOrbit: Sogar aktuelle Atom-CPUs unterstützen den 64-Bit-Modus. x86-32 ist seit Jahren veraltet. Die zusätzlichen Register und die Registeraufruf-ABI sind im typischen Code etwa 10 bis 15% Leistung wert. Wenn Sie neuen Code schreiben, sollte es 64bit sein. Wenn Sie überhaupt eine 32-Bit-Version erstellen, dient dies nur dazu, Benutzer mit wirklich schlechter Hardware zu unterstützen. –

+2

Gute Trauer öffne deinen Geist; wir programmieren nicht alle brandneue Standardhardware für den Verbrauchermarkt. Woher kommt _infrastructure_? –

3

Wie ich im Kommentar oben erwähnt, ist dies wahrscheinlich ein Haufen fragmentation Problem. Der Heap verwaltet Listen von Blöcken unterschiedlicher Größe, um unterschiedliche Speicheranforderungen zu erfüllen. Größere Speicherblöcke werden für kleinere Speicheranfragen in kleinere Blöcke aufgeteilt, um zu vermeiden, dass der Unterschied zwischen der Chunk-Größe und der Anforderungsgröße verschwendet wird, was die Anzahl größerer Chunks verringert. Wenn also ein größerer Chunk angefordert wird, verfügt der Heap möglicherweise nicht über genügend große Chunks, um die Anforderung zu erfüllen.

Die Fragmentierung ist ein großes Problem bei Heap-Implementierungen, da sie den nutzbaren Speicher effektiv reduziert. Einige Heap-Implementierungen sind jedoch in der Lage, kleinere Chunks wieder in größere Chunks zusammenzufassen und sind in der Lage, auch nach einer Anzahl kleinerer Anfragen größere Anforderungen zu erfüllen.

lief ich Ihren obigen Code, sehr leicht modifizierte, Glibc mit malloc (ptmalloc) und ich die folgenden Ergebnisse erhielt ...

Test with large memory blocks (16MB): 
run #0 max mem block   = 2048MB 
run #0 frag mem blocks of 16MB = 2032MB 

run #1 max mem block   = 2048MB 
run #1 frag mem blocks of 16MB = 2032MB 

run #2 max mem block   = 2048MB 
run #2 frag mem blocks of 16MB = 2032MB 

run #3 max mem block   = 2048MB 
run #3 frag mem blocks of 16MB = 2032MB 

run #4 max mem block   = 2048MB 
run #4 frag mem blocks of 16MB = 2032MB 

Test with small memory blocks (16k): 
run #0 max mem block   = 2048MB 
run #0 frag mem blocks of 16k = 2047.98MB 

run #1 max mem block   = 2048MB 
run #1 frag mem blocks of 16k = 2047.98MB 

run #2 max mem block   = 2048MB 
run #2 frag mem blocks of 16k = 2047.98MB 

run #3 max mem block   = 2048MB 
run #3 frag mem blocks of 16k = 2047.98MB 

run #4 max mem block   = 2048MB 
run #4 frag mem blocks of 16k = 2047.98MB 

So scheint ptmalloc mindestens Fragmentierung gut für dieses spezielle Szenario zu behandeln .

+0

Fragmentierung bedeutet, wenn ich die Blöcke ABCDE zuordnen, dann ACE löschen, haben wir xBxDx und ich kann keinen Block größer als A, C oder E zuweisen. Aber was ich tue ist, dass ich ALLE Blöcke delete, also sollte ich bekomme den Speicher als xxxxx (komplett leer) und die Neuzuordnung sollte funktionieren !!! –

+0

Das Problem ist, dass die Haufen nicht unbedingt wissen, dass ABCDE zusammenhängend sind. Also zum Beispiel, wenn ABCDE in A, B, C, D und E aufgeteilt wird. Wenn A zurückgegeben wird und dann B, weiß der Heap nicht unbedingt, dass B die Adresse nach A ist (es sei denn, der Heap ist so gestaltet, dass er 'gefällt ptmalloc' oder ['tcmalloc'] (https://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html). Können Sie versuchen, zu' tcmalloc' zu verlinken? Nach diesem [thread] (https: //groups.google.com/forum/#!topic/google-perftools/iYKiTSZJeFY), müssen Sie lediglich vor "MSVCRT" auf "tcmalloc" verweisen. – Jason

+0

"Das Problem ist, dass die Haufen das nicht unbedingt wissen ABCDE sind zusammenhängend. "Das wäre ein Showstopper-Fehler in einer Heap-Implementierung, der es für jede echte Arbeit unbrauchbar macht. –

Verwandte Themen