Jak uzyskać zużycie pamięci w czasie wykonywania przy użyciu C ++?

90

Muszę uzyskać informacje o wykorzystaniu pamięci VIRT i RES w czasie wykonywania mojego programu i wyświetlić je.

Co próbowałem do tej pory:

getrusage ( http://linux.die.net/man/2/getrusage )

int who = RUSAGE_SELF; 
struct rusage usage; 
int ret; 

ret=getrusage(who,&usage);

cout<<usage.ru_maxrss;

ale zawsze dostaję 0.

jww
źródło
3
Jest to zależne od systemu - wygląda na to, że Twój system nie obsługuje raportowania maxrss przez getrusage - czy możesz nam powiedzieć, jakiej dystrybucji używasz?
tvanfosson

Odpowiedzi:

79

W Linuksie nigdy nie znalazłem rozwiązania ioctl () . Dla naszych aplikacji zakodowaliśmy ogólną procedurę narzędziową opartą na wczytywaniu plików z / proc / pid . Istnieje wiele takich plików, które dają różne wyniki. Oto ten, na którym się zdecydowaliśmy (pytanie zostało oznaczone jako C ++ i obsłużyliśmy operacje we / wy za pomocą konstrukcji C ++, ale powinno być łatwo przystosowane do procedur C i / o, jeśli zajdzie taka potrzeba):

#include <unistd.h>
#include <ios>
#include <iostream>
#include <fstream>
#include <string>

//////////////////////////////////////////////////////////////////////////////
//
// process_mem_usage(double &, double &) - takes two doubles by reference,
// attempts to read the system-dependent data for a process' virtual memory
// size and resident set size, and return the results in KB.
//
// On failure, returns 0.0, 0.0

void process_mem_usage(double& vm_usage, double& resident_set)
{
   using std::ios_base;
   using std::ifstream;
   using std::string;

   vm_usage     = 0.0;
   resident_set = 0.0;

   // 'file' stat seems to give the most reliable results
   //
   ifstream stat_stream("/proc/self/stat",ios_base::in);

   // dummy vars for leading entries in stat that we don't care about
   //
   string pid, comm, state, ppid, pgrp, session, tty_nr;
   string tpgid, flags, minflt, cminflt, majflt, cmajflt;
   string utime, stime, cutime, cstime, priority, nice;
   string O, itrealvalue, starttime;

   // the two fields we want
   //
   unsigned long vsize;
   long rss;

   stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
               >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
               >> utime >> stime >> cutime >> cstime >> priority >> nice
               >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest

   stat_stream.close();

   long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
   vm_usage     = vsize / 1024.0;
   resident_set = rss * page_size_kb;
}

int main()
{
   using std::cout;
   using std::endl;

   double vm, rss;
   process_mem_usage(vm, rss);
   cout << "VM: " << vm << "; RSS: " << rss << endl;
}
Don Wakefield
źródło
czy masz jakieś gwarancje dotyczące struktury / proc / self / stat na różnych platformach * nix? ... Nie jestem pewien, ale jeśli tak - będzie miło.
bayda,
Cóż, przez lata korzystałem głównie z Solaris, HP-UX i Linuksa. / proc / self / stat wydaje się być linuksowy. Oryginalna wersja powyższego programu miała bloki #if dla Solaris, ponieważ różniła się.
Don Wakefield
Zakładam, że OP troszczy się tylko o Linuksa na podstawie tagowania pytań. Czytanie / proc będzie tak dobre, jak to tylko możliwe. W Solarisie możesz również uzyskać informacje o różnych rzeczach poprzez kstat (chociaż często powiela to, co możesz uzyskać innymi sposobami).
stsquad
Spóźniłem się tylko 10 lat na imprezę, ale czy możesz mi powiedzieć, dlaczego dzielisz vsize przez 1024,0 zamiast 1024?
a_river_in_canada
1
re: why 1024.0?- Mówi kompilatorowi, aby przekonwertował na podwójną FIRST, a następnie podzielił, aby uzyskać podwójny wynik. Drugi wybór: vm_usage = vsize / 1024;najpierw dokonałby dzielenia (tracąc precyzję, jak sugerował @DonWakefield), a następnie przekonwertowałby na double.
Jesse Chisholm
52

David Robert Nadeau umieścił dobrą, niezależną, wieloplatformową funkcję C, aby uzyskać rozmiar zestawu rezydentnego procesu (użycie pamięci fizycznej) na swojej stronie internetowej:

/*
 * Author:  David Robert Nadeau
 * Site:    http://NadeauSoftware.com/
 * License: Creative Commons Attribution 3.0 Unported License
 *          http://creativecommons.org/licenses/by/3.0/deed.en_US
 */

#if defined(_WIN32)
#include <windows.h>
#include <psapi.h>

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/resource.h>

#if defined(__APPLE__) && defined(__MACH__)
#include <mach/mach.h>

#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
#include <fcntl.h>
#include <procfs.h>

#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
#include <stdio.h>

#endif

#else
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS."
#endif





/**
 * Returns the peak (maximum so far) resident set size (physical
 * memory use) measured in bytes, or zero if the value cannot be
 * determined on this OS.
 */
size_t getPeakRSS( )
{
#if defined(_WIN32)
    /* Windows -------------------------------------------------- */
    PROCESS_MEMORY_COUNTERS info;
    GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
    return (size_t)info.PeakWorkingSetSize;

#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
    /* AIX and Solaris ------------------------------------------ */
    struct psinfo psinfo;
    int fd = -1;
    if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
        return (size_t)0L;      /* Can't open? */
    if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
    {
        close( fd );
        return (size_t)0L;      /* Can't read? */
    }
    close( fd );
    return (size_t)(psinfo.pr_rssize * 1024L);

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
    /* BSD, Linux, and OSX -------------------------------------- */
    struct rusage rusage;
    getrusage( RUSAGE_SELF, &rusage );
#if defined(__APPLE__) && defined(__MACH__)
    return (size_t)rusage.ru_maxrss;
#else
    return (size_t)(rusage.ru_maxrss * 1024L);
#endif

#else
    /* Unknown OS ----------------------------------------------- */
    return (size_t)0L;          /* Unsupported. */
#endif
}





/**
 * Returns the current resident set size (physical memory use) measured
 * in bytes, or zero if the value cannot be determined on this OS.
 */
size_t getCurrentRSS( )
{
#if defined(_WIN32)
    /* Windows -------------------------------------------------- */
    PROCESS_MEMORY_COUNTERS info;
    GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
    return (size_t)info.WorkingSetSize;

#elif defined(__APPLE__) && defined(__MACH__)
    /* OSX ------------------------------------------------------ */
    struct mach_task_basic_info info;
    mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
    if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO,
        (task_info_t)&info, &infoCount ) != KERN_SUCCESS )
        return (size_t)0L;      /* Can't access? */
    return (size_t)info.resident_size;

#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
    /* Linux ---------------------------------------------------- */
    long rss = 0L;
    FILE* fp = NULL;
    if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
        return (size_t)0L;      /* Can't open? */
    if ( fscanf( fp, "%*s%ld", &rss ) != 1 )
    {
        fclose( fp );
        return (size_t)0L;      /* Can't read? */
    }
    fclose( fp );
    return (size_t)rss * (size_t)sysconf( _SC_PAGESIZE);

#else
    /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
    return (size_t)0L;          /* Unsupported. */
#endif
}

Stosowanie

size_t currentSize = getCurrentRSS( );
size_t peakSize    = getPeakRSS( );

Aby uzyskać więcej informacji, odwiedź witrynę internetową; zapewnia ona również funkcję uzyskiwania fizycznej wielkości pamięci systemu .

pepper_chico
źródło
2
lepiej dodać #pragma comment(lib, "psapi.lib")do #if defined(_WIN32)zakresu.
Bloodmoon,
1
@Bloodmon co jeśli ktoś używa systemu Windows, ale nie kompilatora Microsoft? Ta pragma spowodowałaby awarię kompilatora.
Adrian
Ten kod używa rusage :: ru_maxrss z getrusage, który OP zgłosił jako niedziałający dla niej.
facetus
21

Stary:

maxrss określa maksymalną dostępną pamięć dla procesu. 0 oznacza, że ​​proces nie jest ograniczony. To, czego prawdopodobnie chcesz, to nieudostępniane wykorzystanie danych ru_idrss.

Nowość: wygląda na to, że powyższe faktycznie nie działa, ponieważ jądro nie wypełnia większości wartości. To, co działa, to pobieranie informacji z proc. Zamiast analizować go samodzielnie, łatwiej jest użyć libproc (część procps) w następujący sposób:

// getrusage.c
#include <stdio.h>
#include <proc/readproc.h>

int main() {
  struct proc_t usage;
  look_up_our_self(&usage);
  printf("usage: %lu\n", usage.vsize);
}

Skompiluj z „ gcc -o getrusage getrusage.c -lproc

Paul de Vrieze
źródło
1
Z wyjątkiem tego, że żadne pole nie jest dostępne w systemie Linux.
jmanning2k
2
To jest niepoprawne. maxrss to szczytowe wykorzystanie pamięci przez proces, a nie maksymalne dostępne - to byłoby getrlimit (RLIMIT_DATA, & rl).
jmanning2k
1
#include <proc/readproc.h>Rozwiązanie pracował wielki dla mnie pod Ubuntu. Musiałem zainstalować pakiet libproc-dev. usage.vm_datajest wystarczająco bliskim przybliżeniem tego, czego potrzebowałem. Twój wybór statystyk pamięci jest udokumentowany tutaj: /usr/include/proc/readproc.hWszystkie te, które wypróbowałem, są w bajtach, a nie na stronach. Nie sądzę, by mój proces wykorzystywał 46 milionów stron. Komentarze, że to rozwiązanie nie działa pod Linuksem, wydają się błędne.
Allan Stokes
2
Prawidłowy linker to: -lprocps
Sembiance,
Działa świetnie, ta odpowiedź powinna być akceptowana!
Pekov
9

W systemie Linux, jeśli możesz sobie pozwolić na koszt czasu wykonywania (do debugowania), możesz użyć valgrind z narzędziem masowym:

http://valgrind.org/docs/manual/ms-manual.html

Jest ciężki, ale bardzo przydatny.

David Cournapeau
źródło
8

Bardziej elegancki sposób dla metody Dona Wakefielda:

#include <iostream>
#include <fstream>

using namespace std;

int main(){

    int tSize = 0, resident = 0, share = 0;
    ifstream buffer("/proc/self/statm");
    buffer >> tSize >> resident >> share;
    buffer.close();

    long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
    double rss = resident * page_size_kb;
    cout << "RSS - " << rss << " kB\n";

    double shared_mem = share * page_size_kb;
    cout << "Shared Memory - " << shared_mem << " kB\n";

    cout << "Private Memory - " << rss - shared_mem << "kB\n";
    return 0;
}
Qsiris
źródło
7

Istniejące odpowiedzi są lepsze, jeśli chodzi o uzyskanie prawidłowej wartości, ale mogę przynajmniej wyjaśnić, dlaczego getrusage nie działa dla Ciebie.

człowiek 2 getrusage:

Powyższa struktura [rusage] została zaczerpnięta z BSD 4.3 Reno. Nie wszystkie pola mają znaczenie w Linuksie. Obecnie (Linux 2.4, 2.6) obsługiwane są tylko pola ru_utime, ru_stime, ru_minflt, ru_majflt i ru_nswap.

jmanning2k
źródło
3

dodatkowo
możesz wywołać polecenie ps system i uzyskać z niego zużycie pamięci.
lub przeczytaj informacje z / proc / pid (zobacz strukturę PIOCPSINFO)

bayda
źródło
PIOCPSINFO nie jest tak naprawdę dostępne na żadnym Linuksie, którego używałem. Czytanie z / proc / pid jest dość powszechne. W odpowiedzi zamieszczę przykładowy kod dla Linuksa ...
Don Wakefield,
tak / proc / pid struktury mogą być różne na różnych platformach * nix, ale jeśli masz PIOCPSINFO, to nie ma znaczenia. Miałem sytuację, kiedy ta struktura nie była zdefiniowana w jakiejś wersji solaris .. W tym przypadku użyłem wyjścia ps.
bayda
2

W twoim systemie jest plik o nazwie /proc/self/statm . System plików proc jest pseudosystemem plików, który zapewnia interfejs do struktur danych jądra. Ten plik zawiera potrzebne informacje w kolumnach zawierających tylko liczby całkowite oddzielone spacjami.

Kolumna nr:

  1. = całkowity rozmiar programu (VmSize in / proc / [pid] / status)

  2. = rozmiar zestawu rezydenta (VmRSS w / proc / [pid] / status)

Aby uzyskać więcej informacji, zobacz LINK .

Jakub Krawczuk
źródło
1

Używam innego sposobu, żeby to zrobić i brzmi to realistycznie. To, co robię, to mam PID procesu przez funkcję getpid (), a następnie używam pliku / proc / pid / stat. Wydaje mi się, że 23. kolumna pliku stat to vmsize (spójrz na post Don). Możesz odczytać vmsize z pliku w dowolnym miejscu w kodzie. Jeśli zastanawiasz się, jak bardzo fragment kodu może zużywać pamięć, możesz przeczytać ten plik raz przed tym fragmentem i raz po nim i możesz je od siebie odjąć.


źródło
1

Oparty na rozwiązaniu Dona W, z mniejszą liczbą zmiennych.

void process_mem_usage(double& vm_usage, double& resident_set)
{
    vm_usage     = 0.0;
    resident_set = 0.0;

    // the two fields we want
    unsigned long vsize;
    long rss;
    {
        std::string ignore;
        std::ifstream ifs("/proc/self/stat", std::ios_base::in);
        ifs >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore
                >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore
                >> ignore >> ignore >> vsize >> rss;
    }

    long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
    vm_usage = vsize / 1024.0;
    resident_set = rss * page_size_kb;
}
ϹοδεMεδιϲ
źródło
0

Szukałem aplikacji dla systemu Linux do pomiaru maksymalnej używanej pamięci. Valgrind to doskonałe narzędzie, ale dał mi więcej informacji, niż chciałem. tstime wydawało się być najlepszym narzędziem, jakie mogłem znaleźć. Mierzy użycie pamięci „na dużą skalę” (RSS i wirtualna). Zobacz tę odpowiedź .

jtpereyda
źródło