2012-11-21 19 views
12

Ich stieß auf das folgende Problem mit meinem Code: Ich verwendete Valgrind und gperftools, um Heap-Überprüfung und Heap-Profiling durchzuführen, um zu sehen, ob ich den gesamten Speicher freigeben. Die Ausgabe dieser Werkzeuge sieht gut aus und es scheint, dass ich nicht die Erinnerung verliere. Wenn ich jedoch auf top und die Ausgabe von ps schaue, bin ich verwirrt, weil das im Grunde nicht das darstellt, was ich mit valgrind und gperftools beobachte.Speicherverbrauch in C++ verfolgen und Speicherverbrauch auswerten

Hier sind die Zahlen:

  • Top berichtet: RES 150M
  • Valgrind (Massiv) berichtet: 23M Spitzennutzungs
  • gperftools Heap-Profiler berichtet: 22,7 Spitzenauslastung

Meine Frage ist jetzt, wo t Der Unterschied kommt von? Ich habe auch versucht, die Stack-Nutzung in Valgrind zu verfolgen, aber ohne Erfolg.

Einige weitere Details:

  • Der Prozess ist im Grunde
  • Durchführen einer Dichtigkeitsprüfung und brechen kurz nach dem Laden fertig ist, zeigt ein über die C api zu einer In-Memory-Speicherdaten aus mysql Laden definitive verloren von 144 Bytes und 10M erreichbar, die der Menge entspricht, die derzeit zugeordnet ist
  • Die Bibliothek führt keine komplexe IPC, es startet ein paar Threads, aber nur einer der Threads führt die Arbeit
  • Es lädt nicht andere komplexe Systembibliothek ies
  • die PSS Größe von/proc/pid/smaps entspricht der Größe RES in TOP und ps

Haben Sie irgendwelche Ideen haben, wo dieser Unterschied in der berichteten Speicherverbrauch herkommt? Wie kann ich überprüfen, ob sich mein Programm korrekt verhält? Haben Sie Ideen, wie ich dieses Thema näher untersuchen könnte?

+1

könnte der Unterschied auf Valgrind selbst zurückzuführen sein? –

+1

Ich sammelte die RES und PSS Größen, wenn Valgrind oder gperftools nicht laufen, also nein. – grundprinzip

+1

'RES - Residentgröße (kb) Der nicht-getauschte physische Speicher, den eine Aufgabe verwendet hat." Dumme Frage, ich denke, aber macht free() immer RES? Ein anderer Typ, der auch ähnliche Probleme hat, denke ich, unter http://stackoverflow.com/questions/12262146/free-can-not-release-the-mem-and-dece-the-val-of-res-column-of- top –

Antwort

14

Endlich war ich in der Lage, das Problem zu lösen und werde gerne meine Ergebnisse teilen. Im Allgemeinen ist das beste Werkzeug, um Speicherverbrauch eines Programms aus meiner Perspektive zu bewerten, das Massif Werkzeug von Valgrind. Sie können den Heap-Verbrauch profilieren und erhalten eine detaillierte Analyse.

Um Profil der Heap Ihrer Anwendung ausführen valgrind --tool=massif prog jetzt, erhalten Sie grundlegenden Zugriff auf alle Informationen über die typischen Speicherzuordnungsfunktionen wie malloc und Freunde. Um jedoch tiefer zu graben, habe ich die Option --pages-as-heap=yes aktiviert, die dann auch die Informationen über die unterlagerten Systemaufrufe meldet. Um ein Beispiel hier gegeben ist, etwas von meiner Profilerstellungssitzung:

67 1,284,382,720  978,575,360  978,575,360    0   0 
100.00% (978,575,360B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc. 
->87.28% (854,118,400B) 0x8282419: mmap (syscall-template.S:82) 
| ->84.80% (829,849,600B) 0x821DF7D: _int_malloc (malloc.c:3226) 
| | ->84.36% (825,507,840B) 0x821E49F: _int_memalign (malloc.c:5492) 
| | | ->84.36% (825,507,840B) 0x8220591: memalign (malloc.c:3880) 
| | | ->84.36% (825,507,840B) 0x82217A7: posix_memalign (malloc.c:6315) 
| | |  ->83.37% (815,792,128B) 0x4C74F9B: std::_Rb_tree_node<std::pair<std::string const, unsigned int> >* std::_Rb_tree<std::string, std::pair<std::string const, unsigned int>, std::_Select1st<std::pair<std::string const, unsigned int> >, std::less<std::string>, StrategizedAllocator<std::pair<std::string const, unsigned int>, MemalignStrategy<4096> > >::_M_create_node<std::pair<std::string, unsigned int> >(std::pair<std::string, unsigned int>&&) (MemalignStrategy.h:13) 
| | |  | ->83.37% (815,792,128B) 0x4C7529F: OrderIndifferentDictionary<std::string, MemalignStrategy<4096>, StrategizedAllocator>::addValue(std::string) (stl_tree.h:961) 
| | |  | ->83.37% (815,792,128B) 0x5458DC9: var_to_string(char***, unsigned long, unsigned long, AbstractTable*) (AbstractTable.h:341) 
| | |  |  ->83.37% (815,792,128B) 0x545A466: MySQLInput::load(std::shared_ptr<AbstractTable>, std::vector<std::vector<ColumnMetadata*, std::allocator<ColumnMetadata*> >*, std::allocator<std::vector<ColumnMetadata*, std::allocator<ColumnMetadata*> >*> > const*, Loader::params const&) (MySQLLoader.cpp:161) 
| | |  |  ->83.37% (815,792,128B) 0x54628F2: Loader::load(Loader::params const&) (Loader.cpp:133) 
| | |  |   ->83.37% (815,792,128B) 0x4F6B487: MySQLTableLoad::executePlanOperation() (MySQLTableLoad.cpp:60) 
| | |  |   ->83.37% (815,792,128B) 0x4F8F8F1: _PlanOperation::execute_throws() (PlanOperation.cpp:221) 
| | |  |    ->83.37% (815,792,128B) 0x4F92B08: _PlanOperation::execute() (PlanOperation.cpp:262) 
| | |  |    ->83.37% (815,792,128B) 0x4F92F00: _PlanOperation::operator()() (PlanOperation.cpp:204) 
| | |  |     ->83.37% (815,792,128B) 0x656F9B0: TaskQueue::executeTask() (TaskQueue.cpp:88) 
| | |  |     ->83.37% (815,792,128B) 0x7A70AD6: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) 
| | |  |      ->83.37% (815,792,128B) 0x6BAEEFA: start_thread (pthread_create.c:304) 
| | |  |      ->83.37% (815,792,128B) 0x8285F4B: clone (clone.S:112) 
| | |  |       
| | |  ->00.99% (9,715,712B) in 1+ places, all below ms_print's threshold (01.00%) 
| | |  
| | ->00.44% (4,341,760B) in 1+ places, all below ms_print's threshold (01.00%) 

Wie Sie ~ 85% meiner Speicherzuweisung von einem einzelnen Zweig und der Frage kommen sehen jetzt, warum der Speicherverbrauch so hoch ist, wenn die Original Heap Profiling zeigte einen normalen Verbrauch. Wenn Sie sich das Beispiel ansehen, werden Sie sehen warum. Für die Zuweisung habe ich posix_memalign verwendet, um sicherzustellen, dass Zuweisungen zu nützlichen Grenzen passieren. Dieser Zuordner wurde dann von der äußeren Klasse an die inneren Mitgliedsvariablen (in diesem Fall eine Zuordnung) übergeben, um den Zuordner für die Heapzuweisung zu verwenden. Allerdings war die Grenze, die ich wählte, zu groß - 4096 - in meinem Fall. Das heißt, Sie ordnen 4b unter Verwendung von posix_memalign zu, aber das System weist Ihnen eine ganze Seite zu, um es korrekt auszurichten. Wenn Sie jetzt viele kleine Werte zuweisen, werden Sie viel ungenutzten Speicher haben. Dieser Speicher wird von normalen Heap-Profiling-Tools nicht gemeldet, da Sie nur einen Bruchteil dieses Speichers zuweisen, die Systemzuweisungsroutinen jedoch mehr zuweisen und den Rest verbergen.

Um dieses Problem zu lösen, wechselte ich auf eine kleinere Grenze und konnte so den Speicheraufwand drastisch reduzieren.

Als eine Zusammenfassung meiner Stunden vor dem Massif & Co. kann ich nur empfehlen, dieses Tool für tiefes Profiling zu verwenden, da es Ihnen ein sehr gutes Verständnis dessen gibt, was passiert und Trackingfehler leicht erlaubt. Für die Verwendung von posix_memalign ist die Situation anders. Es gibt Fälle, in denen es wirklich notwendig ist, aber für die meisten Fälle werden Sie mit einem normalen malloc gerecht.

0

Standardmäßig meldet Massif nur die Heap-Größe. TOP gibt die tatsächliche Größe im Speicher an, einschließlich der Größe, die vom Programmcode selbst verwendet wird, sowie der Stapelgröße.

Versuchen Sie, Massif mit der Option --stacks=yes zu versorgen, um die Gesamtspeicherauslastung anzugeben, einschließlich Stapelspeicherplatz und sehen Sie, ob sich dadurch das Bild ändert?

+0

Ich habe das mit Massif und der Stackgröße überprüft, wie oben gesagt, aber die Stackgröße ist konstant um 15k. – grundprinzip

1

Nach Artikel this Artikel PS/Top-Bericht, wie viel Speicher Ihr Programm verwendet, wenn es das einzige Programm ausgeführt wurde. Angenommen, dass Ihr Programm z.B. verwendet eine Reihe von gemeinsam genutzten Bibliotheken wie STL, die bereits in den Arbeitsspeicher geladen sind, gibt es eine Lücke zwischen der Menge an tatsächlichem Arbeitsspeicher, der aufgrund der Ausführung Ihres Programms zugewiesen wird, vs. wieviel Arbeitsspeicher es zuweisen würde, wenn es der einzige Prozess wäre.

+0

Der Artikel ist, wo ich den Tipp über PSS-Größe bekommen habe, also habe ich das überprüft. Mit weiteren Untersuchungen, bin ich jetzt an einem Punkt, wo ich einige der Unterschiede in Speicherverbrauch kommt von der zugrunde liegenden Malloc() - Implementierung, die größere Blöcke und einige Seltsamkeit in 'const char *' zu 'std :: string 'Konvertierungen zuweisen wird . Valgrinds-Massiv mit der Option '--pages-as-heap = yes' hat mir sehr geholfen. – grundprinzip

+0

Ich sehe. Nur eine Idee für weitere Untersuchungen: Erhöhen Sie die Menge an Speicher, die Sie in Ihrem Programm zuweisen (z. B. 24 MB -> 512 MB -> 1024 MB) und beobachten Sie, ob die undefinierte Differenz zur oberen/ps-Ausgabe konstant bleibt oder auch wächst. – Christian