2012-11-21 16 views
12

Natknąłem się na następujący problem z moim kodem: Używałem Valgrind i gperftools do sprawdzania sterty i profilowania sterty, aby sprawdzić, czy zwolnię całą przydzieloną przeze mnie pamięć. Wydajność tych narzędzi wygląda dobrze i wygląda na to, że nie tracę pamięci. Jednak gdy patrzę na top i na wyjście ps jestem zdezorientowany, ponieważ w zasadzie nie reprezentuje tego, co obserwuję z valgrind i gperftools.Śledź użycie pamięci w C++ i oceń zużycie pamięci

Oto numery:

  • Top raportów: RES 150M
  • Valgrind (Massif) sprawozdania: 23m użytkowania szczyt
  • gperftools Heap Profiler donosi: 22,7 mln Wykorzystanie szczyt

Moje pytanie brzmi teraz, gdzie ma Różnica pochodzi? Próbowałem również śledzić użycie stosu w Valgrind, ale bez powodzenia.

trochę więcej szczegółów:

  • Proces jest w zasadzie ładowanie danych z mysql poprzez API C do przechowywania
  • przeprowadza kontrolę szczelności i łamiącym krótko po załadunek odbywa się w pamięci, przedstawia ostateczna utrata 144 bajtów i 10M osiągalna, która pasuje do aktualnie przydzielonej kwoty
  • Biblioteka nie wykonuje skomplikowanego IPC, uruchamia kilka wątków, ale tylko jeden z wątków wykonuje pracę
  • Nie ładuje się inny złożony system bibliotekarski ies
  • wielkość PSS z/proc/pid/smaps odpowiada rozmiarowi OZE w TOP i PS

Czy macie jakieś pomysły, gdzie ta różnica w omawianym zużycie pamięci pochodzi? Jak mogę sprawdzić, czy mój program zachowuje się poprawnie? Czy masz jakieś pomysły na dalsze badanie tego problemu?

+1

Czy różnica może wynikać z samego valgrind? –

+1

Zebrałem rozmiary OZE i PSS, gdy nie używam Valgrind lub gperftools, więc nie. – grundprinzip

+1

'RES - Wielkość rezydentna (kb) Pamięć fizyczna, która nie została zamieniona, zadanie zostało użyte" Głupie pytanie, jak sądzę, ale czy funkcja bezpłatna() zawsze redukuje OZE? Inny facet również ma podobne problemy, jak sądzę, na http://stackoverflow.com/questions/12262146/free-can-not-release-the-mem-and-decrease-the-value-of-res-column-of- top –

Odpowiedz

14

W końcu udało mi się rozwiązać problem i chętnie podzielę się moimi odkryciami. Ogólnie najlepszym narzędziem do oceny wykorzystania pamięci przez program z mojej perspektywy jest narzędzie z Valgrind. pozwala na profilowanie zużycia sterty i daje szczegółową analizę.

Aby teraz odtworzyć stertę aplikacji pod numerem valgrind --tool=massif prog, uzyskasz podstawowy dostęp do wszystkich informacji o typowych funkcjach przydzielania pamięci, takich jak malloc i znajomych. Jednakże, aby kopać głębiej, aktywowałem opcję --pages-as-heap=yes, która następnie będzie raportować nawet informacje o wywoływanych wywołaniach systemu. Aby dany przykład tutaj jest coś z mojej sesji profilowania:

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%) 

Jak widać ~ 85% mojego alokacji pamięci pochodzą z jednego oddziału i pytanie jest dlaczego zużycie pamięci jest tak wysoka, jeśli oryginalne profilowanie sterty wykazało normalne zużycie. Jeśli spojrzysz na przykład, zobaczysz dlaczego. W celu przydzielenia użyłem posix_memalign, aby upewnić się, że alokacje trafiają do przydatnych granic. Ten alokator został następnie przekazany z klasy zewnętrznej do zmiennych wewnętrznych (w tym przypadku mapa), aby użyć alokatora do alokacji sterty. Jednak granica, którą wybrałem, była zbyt duża - 4096 - w moim przypadku. Oznacza to, że przydzielisz 4b przy użyciu posix_memalign, ale system przydzieli całą stronę, abyś mógł ją poprawnie wyrównać. Jeśli teraz przydzielisz wiele małych wartości, otrzymasz mnóstwo nieużywanej pamięci. Ta pamięć nie zostanie zgłoszona przez zwykłe narzędzia do profilowania sterty, ponieważ alokujesz tylko ułamek tej pamięci, ale procedury alokacji systemu przydzieliją więcej i ukryją resztę.

Aby rozwiązać ten problem, przełączyłem się na mniejszą granicę i mogłem radykalnie zmniejszyć obciążenie pamięci.

Jako zakończenie moich godzin spędzonych przed Massifem & Co mogę tylko polecić, aby użyć tego narzędzia do głębokiego profilowania, ponieważ daje bardzo dobre zrozumienie tego, co się dzieje i pozwala łatwo śledzić błędy. W przypadku korzystania z posix_memalign sytuacja jest inna. Zdarzają się przypadki, w których jest to naprawdę konieczne, jednak w większości przypadków wszystko będzie dobrze z normalnym malloc.

0

Domyślnie Massif zgłasza tylko wielkość sterty. TOP podaje rzeczywisty rozmiar w pamięci, w tym rozmiar używany przez sam kod programu, a także rozmiar stosu.

Spróbuj podać masyw z opcją --stacks=yes, informując, aby zgłosić całkowite zużycie pamięci, w tym miejsce na stos i sprawdzić, czy to zmienia obraz?

+0

Zrobiłem sprawdzanie z masywem i rozmiarem stosu, jak wspomniano powyżej, ale rozmiar stosu jest stały około 15k. – grundprinzip

1

Zgodnie z this artykuł ps/top informuje o ilości pamięci używanej przez program, jeśli był to jedyny uruchomiony program. Zakładając, że Twój program, np. korzysta z wielu współdzielonych bibliotek, takich jak STL, które są już załadowane do pamięci, istnieje luka między ilością rzeczywistej pamięci, która jest alokowana z powodu wykonania twojego programu w stosunku do ilości pamięci przydzielonej, gdyby był to jedyny proces.

+0

Artykuł na ten temat zawiera informacje na temat rozmiaru PSS, więc to sprawdziłem. Po dalszych badaniach, jestem teraz w punkcie, w którym część różnicy w zużyciu pamięci pochodzi z implementacji malloc() underlaying, która przydzieli większe bloki i trochę dziwności w konwersji "const char *" na 'std :: string' . Masyw Valgrinds z opcją '--pages-as-heap = yes' bardzo pomógł. – grundprinzip

+0

Rozumiem. Po prostu pomysł na dalsze badania: zwiększ ilość pamięci przydzielonej w programie (np. 24 MB -> 512 MB -> 1024 MB) i obserwuj, czy niezdefiniowana różnica w stosunku do górnego/psowego wyjścia pozostaje stała lub też rośnie. – Christian