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.
könnte der Unterschied auf Valgrind selbst zurückzuführen sein? –
Ich sammelte die RES und PSS Größen, wenn Valgrind oder gperftools nicht laufen, also nein. – grundprinzip
'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 –