3

I am trying to measure the amount of memory used by a child process via the getrusage system call with the following code

#include <iostream>
using std::cout;
using std::endl;
#include <unistd.h>
#include <thread>
#include <chrono>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <cassert>
#include <vector>

int main() {

    auto child_pid = fork();
    if (!child_pid) {
        cout << "In child process with process id " << getpid() << endl;
        cout << "Child's parent process is " << getppid() << endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));

        std::vector<int> vec;
        vec.resize(10000);
        for (auto ele : vec) {
            cout << ele << endl;
        }

    } else {
        // this will wait for the child above to finish
        waitpid(child_pid, nullptr, 0);
        struct rusage usage;
        int return_val_getrusage = getrusage(RUSAGE_CHILDREN, &usage);
        assert(!return_val_getrusage);
        cout << "Memory used by child " << usage.ru_maxrss << endl;
    }

    return 0;
}

I keep changing the amount of memory I am allocating by putting in different arguments to the vector::resize() call. This however always prints a value around 2300 for memory usage by child. I am not sure this is the right way to go about measuring memory usage for a child process. Even if I add calls to getrusage with RUSAGE_SELF in the child process before the allocation of the vector, the value of ru_maxrss remains the same. Could someone tell me what I can do better here?

Curious
  • 20,870
  • 8
  • 61
  • 146
  • Try to add some randomization to the vector size, and it might change. Otherwise the size of the child process and its heap will be the same all the time. – Some programmer dude Mar 05 '16 at 08:07
  • @JoachimPileborg Sorry for not being clear at all. I have been changing the argument to the `vector::resize()` each time I run the program and the output is still the same... – Curious Mar 05 '16 at 08:15
  • Then maybe the heap isn't counted? Try creating a global array variable instead, to increase the data segment size. Or how about executing different programs? – Some programmer dude Mar 05 '16 at 08:18
  • 2300 kb that's more than sufficient to store 10 000 ints. Try with 600 000. – Christophe Mar 05 '16 at 09:06
  • 1
    Why should anything be left after the child finishes? – Ulrich Eckhardt Mar 05 '16 at 09:30
  • @UlrichEckhardt good point! No memory is assigned to dead processes! i've understood however that the code snippet is only one of many attempts, as OP said he would get same results if called from child – Christophe Mar 05 '16 at 10:14
  • Well, then my advise is to reduce this to the smallest example that shows unexpected behaviour or maybe even several examples. Guessing the answer to an unclear question concerning an unclear problem isn't going to help though. – Ulrich Eckhardt Mar 05 '16 at 11:12
  • 1
    @UlrichEckhardt According to the FreeBSD man pages `Getrusage() returns information describing the resources utilized by the current process, or all its terminated child processes.`, therefore it is okay to query the memory used by a child after it has died – Curious Mar 05 '16 at 17:19
  • @Curios indeed ! This is even a [posix](http://pubs.opengroup.org/onlinepubs/009695399/functions/getrusage.html) requirement so applicable to linux and other systems as well. It is however not clear to me (your feedback would be very appreciated) if it's the maximum memory ever requested by the children or the last state. In the latter case (which I suspect), you have to take into consideration that the local vector will be automatically destroyed before returning to OS, so that the implementation could give back some memory (implementation dependent). – Christophe Mar 05 '16 at 17:47

1 Answers1

3

The internal management of heap and freestore are implementation defined and depend on underlying operating system.

Usually for performance reasons not every allocation will result in more space requested from os: the standard library will pool some process memory and will extend the pool only if no block of sufficient size is found.

I therefore suppose that the varrying sizes you've tried are still within the couple of MBs allocated at start. You should try really big allocations to find a difference.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thank you for your answer! It makes sense. However I have one further question - Getting to the "right" way to measure memory usage took me quite some time to figure out. It was rather difficult to stumble upon the right man page even after researching questions like this extensively on stack overflow. How should one go about figuring out answers to systems problems like this one in general? – Curious Mar 05 '16 at 17:28
  • Someone more experienced than me will probably know better! – Curious Mar 05 '16 at 17:28
  • 1
    I'd measure the usage for the current process only. Use of `getrusage()` is perfectly valid if you want to see memory allocation from OS perspective (aka memory allocated to process vs. memory available). If you want however to have a fine grained analysis of the memory used by your code you'd need to use some tools/libraries for memory debugging (ex: [Valgrind](http://www.cprogramming.com/debugging/valgrind.html), MemTrack, or [others](http://stackoverflow.com/questions/438515/how-to-track-memory-allocations-in-c-especially-new-delete) – Christophe Mar 05 '16 at 17:57
  • I meant to ask more on the lines of "how can I go about learning about more POSIX facilities like this without starting right at the manual pages?" but thanks for your answer – Curious Mar 05 '16 at 22:41