Como obter o uso de memória em tempo de execução usando C ++?

90

Preciso obter o VIRT e RES do uso do mem em tempo de execução do meu programa e exibi-los.

O que eu tentei até agora:

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;

mas sempre recebo 0.

jww
fonte
3
Isso depende do sistema - parece que seu sistema não oferece suporte para relatórios maxrss via getrusage - você pode nos dizer qual distribuição está usando?
tvanfosson,

Respostas:

79

No Linux, nunca encontrei uma solução ioctl () . Para nossos aplicativos, codificamos uma rotina de utilitário geral baseada na leitura de arquivos em / proc / pid . Existem vários desses arquivos que apresentam resultados diferentes. Aqui está o que decidimos (a questão foi marcada como C ++, e lidamos com I / O usando construções C ++, mas deve ser facilmente adaptável a rotinas C i / o se você precisar):

#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
fonte
você tem alguma garantia sobre a estrutura / proc / self / stat em diferentes plataformas * nix? ... Não tenho certeza, mas se sim - será bom.
bayda de
Bem, ao longo dos anos, usei principalmente Solaris, HP-UX e Linux. / proc / self / stat parece ser um Linux-ismo. A versão original do programa acima tinha blocos #if para Solaris, pois era diferente.
Don Wakefield
Estou assumindo que o OP só se preocupa com o Linux com base na marcação de perguntas. Ler / proc será tão bom quanto você conseguirá. No Solaris, você também pode obter informações sobre todos os tipos de coisas por meio do kstat (embora ele frequentemente replique o que você pode obter por outros meios).
stsquad de
Estou apenas 10 anos atrasado para a festa, mas você se importaria de me dizer por que você divide o vsize por 1024,0 em vez de 1024?
a_river_in_canada
1
re: why 1024.0?- Diz ao compilador para converter para double FIRST e então fazer a divisão para obter o resultado duplo. A outra escolha: vm_usage = vsize / 1024;faria a divisão primeiro (perdendo a precisão como @DonWakefield sugeriu) e depois converteria para o dobro.
Jesse Chisholm
52

David Robert Nadeau colocou uma boa função C multi-plataforma independente para obter o tamanho do conjunto residente do processo (uso de memória física) em seu site:

/*
 * 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
}

Uso

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

Para mais discussão, verifique o site, ele também fornece uma função para obter o tamanho da memória física de um sistema .

pepper_chico
fonte
2
é melhor adicionar #pragma comment(lib, "psapi.lib")ao #if defined(_WIN32)escopo.
Bloodmoon
1
@Bloodmon e se alguém estiver usando o Windows, mas não um compilador da Microsoft? Esse pragma faria o compilador falhar.
Adrian
Este código usa rusage :: ru_maxrss de getrusage, que o OP relatou como não funcionando para ela.
facetus
22

Velho:

maxrss indica a memória máxima disponível para o processo. 0 significa que nenhum limite é colocado no processo. O que você provavelmente deseja é o uso de dados não compartilhados ru_idrss.

Novo: Parece que o acima não funciona, pois o kernel não preenche a maioria dos valores. O que funciona é obter as informações do proc. Em vez de analisá-lo sozinho, é mais fácil usar libproc (parte do procps) da seguinte maneira:

// 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);
}

Compilar com " gcc -o getrusage getrusage.c -lproc"

Paul de Vrieze
fonte
1
Exceto que nenhum dos campos está disponível no Linux.
jmanning2k
2
Isso está incorreto. maxrss é o pico de uso de memória do processo, não o máximo disponível - que seria getrlimit (RLIMIT_DATA, & rl).
jmanning2k
2
A #include <proc/readproc.h>solução funcionou muito bem para mim no Ubuntu. Tive que instalar o pacote libproc-dev. usage.vm_dataé uma aproximação suficiente do que eu precisava. Sua escolha de estatística de memória está documentada aqui: /usr/include/proc/readproc.hTodos os que tentei parecem estar em bytes, não em páginas. Não acho que meu processo estava usando 46 milhões de páginas. Os comentários de que esta solução não funciona no Linux parecem equivocados.
Allan Stokes
2
O linker correto é: -lprocps
Sembiance
1
Funciona muito bem, esta deve ser a resposta aceita!
Pekov,
9

No Linux, se você pode pagar o custo de tempo de execução (para depuração), você pode usar valgrind com a ferramenta massif:

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

É pesado, mas muito útil.

David Cournapeau
fonte
8

Uma maneira mais elegante para o método Don Wakefield:

#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
fonte
7

As respostas existentes são melhores para saber como obter o valor correto, mas posso pelo menos explicar por que getrusage não está funcionando para você.

man 2 getrusage:

A estrutura acima [rusage] foi retirada do BSD 4.3 Reno. Nem todos os campos são significativos no Linux. No momento (Linux 2.4, 2.6) apenas os campos ru_utime, ru_stime, ru_minflt, ru_majflt e ru_nswap são mantidos.

jmanning2k
fonte
3

além da sua maneira,
você pode chamar o comando system ps e obter o uso de memória de sua saída.
ou leia as informações em / proc / pid (consulte a estrutura PIOCPSINFO)

Bayda
fonte
PIOCPSINFO não está realmente disponível em nenhum Linux que usei. Ler de / proc / pid é bastante comum. Vou postar código de exemplo para Linux em uma resposta ...
Don Wakefield,
Sim, as estruturas / proc / pid podem ser diferentes em diferentes plataformas * nix, mas se você tiver PIOCPSINFO, não importa. Eu tive uma situação em que esta estrutura não foi definida em alguma versão do solaris .. Usei a saída do ps neste caso.
bayda
2

No seu sistema existe um arquivo chamado /proc/self/statm . O sistema de arquivos proc é um pseudo-sistema de arquivos que fornece uma interface para as estruturas de dados do kernel. Este arquivo contém as informações de que você precisa em colunas apenas com números inteiros separados por espaço.

Número da coluna:

  1. = tamanho total do programa (VmSize em / proc / [pid] / status)

  2. = tamanho do conjunto residente (VmRSS em / proc / [pid] / status)

Para obter mais informações, consulte o LINK .

Jakub Krawczuk
fonte
1

Estou usando outra maneira de fazer isso e parece realista. O que eu faço é obter o PID do processo pela função getpid () e usar o arquivo / proc / pid / stat. Eu acredito que a 23ª coluna do arquivo stat é o vmsize (veja o post de Don). Você pode ler o vmsize do arquivo sempre que precisar no código. Caso você esteja se perguntando quanto um trecho de código pode usar a memória, você pode ler esse arquivo uma vez antes do trecho e uma vez depois e pode subtraí-los um do outro.


fonte
1

Baseado na solução de Don W, com menos variáveis.

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εδιϲ
fonte
0

Eu estava procurando um aplicativo Linux para medir a memória máxima usada. valgrind é uma ferramenta excelente, mas estava me dando mais informações do que eu queria. O tempo parecia ser a melhor ferramenta que pude encontrar. Ele mede o uso de memória "highwater" (RSS e virtual). Veja esta resposta .

Jtpereyda
fonte