0

I would like to measure the maximum memory usage of abc.exe on random tests generated by gen.exe. How could I do that?

My code that runs abc.exe on tests from gen.exe looks like this:

#include <bits/stdc++.h> using namespace std; int main() { int i = 0; while (true) { string si = to_string(i); cout << i << "\n"; if (system(("echo " + si + "| ./gen.exe > test.in").c_str())) // gen.exe is test generator { cout << "gen error\n"; break; } if (system(("./abc.exe < test.in > a.out"))) // abc.exe is the program I want to test { cout << "abc error\n"; break; } i++; } } 

I know that i can use time -v ./abc.exe but then the used memory is printed in the terminal but I'd like to be able to save it to a variable.

5
  • 5
    Side notes: (1) #include <bits/stdc++.h>: stackoverflow.com/questions/31816095/…, (2) using namespace std;: stackoverflow.com/questions/1452721/… Commented Jan 14, 2023 at 16:01
  • why in c++ rather than say in python or bash? Commented Jan 14, 2023 at 16:25
  • @NeilButterworth Can this be done in bash? Commented Jan 14, 2023 at 16:34
  • 1
    sure, why not? python would probably be nicer, though. Commented Jan 14, 2023 at 16:38
  • Use popen() to run another application and catch the standard output in a stream. man popen Commented Jan 14, 2023 at 18:14

1 Answer 1

1

You can use getrusage( RUSAGE_CHILDREN, ... ) to obtain the maximum resident memory. Note that this call will return the maximum memory used by the biggest child at that point in time.

In the example below I used boost::process because it gives better control but it's up to you to use std::system or not, works the same way.

#include <string> #include <cstdint> #include <string.h> #include <iostream> #include <boost/process/child.hpp> #include <sys/resource.h> namespace bp = boost::process; int parent( const std::string& exename ) { // Loop from 0 to 10 megabytes for ( int j=0; j<10; ++j ) { // Command name is the name of this executable plus one argument with size std::string gencmd = exename + " " + std::to_string(j); // Start process bp::child child( gencmd ); // Wait for it to allocate memory sleep(1); // Query the memory usage at this point in time struct rusage ru; getrusage( RUSAGE_CHILDREN, &ru ); std::cerr << "Loop:" << j << " mem:"<< ru.ru_maxrss/1024. << " MB" << std::endl; // Wait for process to quit child.wait(); if ( child.exit_code()!=0 ) { std::cerr << "Error executing child:" << child.exit_code() << std::endl; return 1; } } return 0; } int child( int size ) { // Allocated "size" megabites explicitly size_t memsize = size*1024*1024; uint8_t* ptr = (uint8_t*)malloc( memsize ); memset( ptr, size, memsize ); // Wait for the parent to sample our memory usage sleep( 2 ); // Free memory free( ptr ); return 0; } int main( int argc, char* argv[] ) { // Without arguments, it is the parent. // Pass the name of the binary if ( argc==1 ) return parent( argv[0] ); return child( std::atoi( argv[1] ) ); } 

It prints

$ ./env_test Loop:0 mem:0 MB Loop:1 mem:3.5625 MB Loop:2 mem:4.01953 MB Loop:3 mem:5.05469 MB Loop:4 mem:6.04688 MB Loop:5 mem:7.05078 MB Loop:6 mem:7.78516 MB Loop:7 mem:8.97266 MB Loop:8 mem:9.82031 MB Loop:9 mem:10.8867 MB 

If you cannot use boost libraries, you'd got to work a little more but it is still feasible.

If you just want to know the maximum size ever of your children processes then the following works with std::system:

#include <cstdio> #include <string> #include <iostream> #include <sstream> #include <string.h> #include <unistd.h> #include <sys/resource.h> int main(int argc, char* argv[]) { if (argc > 1) { size_t size = ::atol(argv[1]); size_t memsize = size * 1024 * 1024; void* ptr = ::malloc(memsize); memset(ptr, 0, memsize); ::sleep(2); ::free(ptr); return 0; } for (int j = 0; j < 10; ++j) { std::ostringstream cmd; cmd << argv[0] << " " << j; int res = std::system(cmd.str().c_str()); if (res < 0) { fprintf(stderr, "ERROR system: %s\n", strerror(errno)); break; } struct rusage ru; res = getrusage(RUSAGE_CHILDREN, &ru); size_t maxmem = ru.ru_maxrss; fprintf(stderr, "Loop:%d MaxMem:%ld\n", j, maxmem); } return 0; } 

It prints

Loop:0 MaxMem:3552 Loop:1 MaxMem:4192 Loop:2 MaxMem:5148 Loop:3 MaxMem:6228 Loop:4 MaxMem:7364 Loop:5 MaxMem:8456 Loop:6 MaxMem:9120 Loop:7 MaxMem:10188 Loop:8 MaxMem:11324 Loop:9 MaxMem:12256 

However if you want to keep track of the memory usage during the child process execution you cannot use std::system(). First, you need to call fork() to spawn a new process and then execv() to execute a bash command.

#include <string> #include <cstdint> #include <string.h> #include <unistd.h> #include <iostream> #include <sys/resource.h> #include <sys/wait.h> #include <fcntl.h> #include <vector> int parent(const std::string& exename) { // Loop from 0 to 10 megabytes for (int j = 0; j < 10; ++j) { // Command name is the name of this executable plus one argument with size std::string gencmd = exename + " " + std::to_string(j); // Start process pid_t pid = fork(); if (pid == 0) { // child const char* args[] = {"/bin/bash", "-c", gencmd.c_str(), (char*)0}; int res = execv("/bin/bash", (char**)args); // Should never return std::cerr << "execv error: " << strerror(errno) << std::endl; return 1; } // parent long maxmem = 0; while (true) { int status; pid_t rid = ::waitpid(pid, &status, WNOHANG); if (rid < 0) { if (errno != ECHILD) { std::cerr << "waitpid:" << strerror(errno) << std::endl; return 2; } break; } if (rid == pid) { if (WIFEXITED(pid)) { break; } } // Wait for it to allocate memory usleep(10000); // Query the memory usage at this point in time struct rusage ru; int res = getrusage(RUSAGE_CHILDREN, &ru); if (res != 0) { if (errno != ECHILD) { std::cerr << "getrusage:" << errno << strerror(errno) << std::endl; } break; } if (maxmem < ru.ru_maxrss) { maxmem = ru.ru_maxrss; } } std::cerr << "Loop:" << j << " mem:" << maxmem / 1024. << " MB" << std::endl; } return 0; } int child(int size) { // Allocated "size" megabites explicitly size_t memsize = size * 1024 * 1024; uint8_t* ptr = (uint8_t*)malloc(memsize); memset(ptr, size, memsize); // Wait for the parent to sample our memory usage sleep(2); // Free memory free(ptr); return 0; } int main(int argc, char* argv[]) { // Without arguments, it is the parent. // Pass the name of the binary if (argc == 1) return parent(argv[0]); return child(std::atoi(argv[1])); } 

The result on my machine is:

$ ./fork_test Loop:0 mem:3.22656 MB Loop:1 mem:3.69922 MB Loop:2 mem:4.80859 MB Loop:3 mem:5.92578 MB Loop:4 mem:6.87109 MB Loop:5 mem:8.05469 MB Loop:6 mem:8.77344 MB Loop:7 mem:9.71875 MB Loop:8 mem:10.7422 MB Loop:9 mem:11.6797 MB 

There is a video about this post.

Sign up to request clarification or add additional context in comments.

2 Comments

Could you write how to do it using std::system? (external libraries cannot be used in competitions)
@Darkolin Added example without boost

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.