bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

C/C++ Memory Usage Code

Mon Mar 11, 2019 2:30 pm

Inspired by this thread viewtopic.php?f=33&t=229992&p=1440320#p1440320, I decided to make a new one for memory usage, as I didn't want to hijack it.

I'm looking for efficient ways to gather and print memory usage in C or C++.

Here is what I have:

mem.cpp:

Code: Select all

/*

  g++ -Wall mem.cpp -o mem 

*/

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

std::string mem() 
{
     
    const std::string info[] = {"Cached:", "Buffers:", "MemFree:", "MemTotal:"};
    int intInfo[4];
     
    std::string token;
    std::ifstream file("/proc/meminfo");
 
    while(file >> token){

        for(short i = 4; i != -1; --i)
        {   
            if(token == info[i])
            {   
                file >> intInfo[i];
                
                if(i == 0){
                    file.close(); 
                    return std::to_string((intInfo[3] - intInfo[2] - (intInfo[1] + intInfo[0])) / 1024) + "m ";
                }

            }
        }

       // ignore rest of the line
        file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');   
    }


    // Couldn't get a reading!
    return "...";
}
     
int main()
{

    // I've seen for(;;) used for infinite loops, but I've found while loops to be more efficient:
    while (true){
        std::cout << "mem: " << mem() << std::endl;
        
        // Used for creating a high load and testing the cpu usage (when adjusted low):
        usleep(1000000);     // micoseconds
        
        // Regular 1 sec interval for everyday use:
        //sleep(1);
        
    }

    return 0;
}
Would like to see other methods and compare. I have a feeling that the one I posted is not that efficient.

jahboater
Posts: 4605
Joined: Wed Feb 04, 2015 6:38 pm

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 3:18 pm

Looks good!
/proc/meminfo is the way to go.
What would we do without procfs!!

A couple of very trivial comments:

Code: Select all

// I've seen for(;;) used for infinite loops, but I've found while loops to be more efficient:
    while (true){
Static program checkers might complain about while(true) saying "constant in conditional context" or similar.
A decent compiler will not care a bit and will produce identical code.
The predecessor language B had a keyword "repeat" for this which I like. In C/C++ it is:

Code: Select all

#define repeat for(;;)
In C++ for indexing an array, you have range based for loops which are cool.
Note that "do while()" loops can be faster than plain while() loops.

Code: Select all

for(short i = 4; i != -1; --i)
            if(token == info[i])
This might be slow, use size_t or ssize_t for array indexing (or at least an int).
a) short must sign-extended up to something the same size as a char*, and b) the short type on Intel requires a prefix.
Readers of the code will not expect "short" and will waste time working out why.
Again, I am sure the compiler will completely ignore your "short i" and use a full sized register!!!

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 7:53 pm

jahboater wrote:
Mon Mar 11, 2019 3:18 pm
Looks good!
/proc/meminfo is the way to go.
What would we do without procfs!!

A couple of very trivial comments:

Note that "do while()" loops can be faster than plain while() loops.
Changed! And /proc/meminfo is so handy.

Code: Select all

for(short i = 4; i != -1; --i)
            if(token == info[i])
This might be slow, use size_t or ssize_t for array indexing (or at least an int).
a) short must sign-extended up to something the same size as a char*, and b) the short type on Intel requires a prefix.
Readers of the code will not expect "short" and will waste time working out why.
Again, I am sure the compiler will completely ignore your "short i" and use a full sized register!!!
Fixed. It was in the back of my mind that this might happen. Thank you for clarifying. size_t is new to me; I had to look the usage of that one up! ;)


Updated code:

mem.cpp

Code: Select all

/*

  g++ -Wall mem.cpp -o mem 

*/

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

std::string mem()
{
    /* Declare 'info[]' static so it doesn't have to reassign each time the function runs? Or does the compiler take care of that? */
    static const std::string info[] = {"MemTotal:", "MemFree:", "Buffers:", "Cached:"}; 
    int intInfo[4];
    std::string token;
    std::ifstream file("/proc/meminfo");
 
    while(file >> token)
    {

        for(std::size_t i = 0; i != 4; i++)
            if(token == info[i])
            {   
                file >> intInfo[i];
                
                if(i == 3){
                    file.close(); 
                    // Went with kB output. Just a personal thing.
                    return std::to_string(intInfo[0] - intInfo[1] - (intInfo[2] + intInfo[3])) + "kB ";
                }
            }

    // ignore rest of the line (Not sure if benefitical)
    // file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');      
    }

    // Something went wrong!
    return "...";
}

int main()
{
    
    do{

        std::cout << "mem: " << mem() << std::endl;

        sleep(1);
        //usleep(1000000);     //microseconds (for testing)

    }while(1); // '1' is better?

    return 0;
}

swampdog
Posts: 230
Joined: Fri Dec 04, 2015 11:22 am

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 10:32 pm

Would like to see other methods and compare. I have a feeling that the one I posted is not that efficient.
Well, as I had some unexpected free time today I thought I'd try the bloaty approach. This is because I watched a youtube vid last night about old games - Dungeon Keeper & Theme Hospital so I've had "bloaty head syndrome" wandering around my mind accompanied by squeaking imp sounds ever since I woke up, plus I wanted to play with that "auto operator<<" template. My exception handling is somewhat frivolous but its existence completes the kitchen sink.. ;-)

Code: Select all

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <algorithm>
#include <sstream>
#include <deque>
#include <cstdlib>
#include <unistd.h>

typedef std::size_t			Siz;
typedef std::string			Str;
typedef std::deque<Str>			ConStr;
typedef std::basic_istringstream<char>	Biss;
typedef std::basic_ostream<char>	Bos;

template <typename CO> void
DumpCon(const CO & co)
{typename CO::const_iterator	i;
 Siz				n;
 
 for    (
        i = co.begin()  ,n = 0;
        i != co.end();
        ++i             ,++n
        )
        std::cout << " [" << n << "]=" << *i << '\n'
 ;
}

ConStr
Load(const Str & fn)
{std::ifstream	ifs	(fn.c_str());
 ConStr		cs;
 Str		s;

 if (!ifs)
	throw "wobbler"
 ;
 while (std::getline(ifs,s))
	cs.push_back(s)
 ;
 if (ifs.bad())
	throw "wobbler"
 ;
 return cs;
}

template<class T>
auto operator<<(Bos& os, const T& t) -> decltype(t.Emit(os), os) 
{
 t.Emit(os);
 return os; 
}

class Bloater	{
typedef std::map<Str,Str>	MSS;
MSS		m;

public:
		Bloater(const ConStr&);
const Str &	operator[](const Str&)const;
Bos &		Emit(Bos&)const;
private:
		Bloater(const Bloater&);
Bloater &	operator=(const Bloater&);
};

Bloater::Bloater(
	const ConStr & cs
)
{
 for	(
	ConStr::const_iterator	i	=cs.begin();
	i != cs.end();
	++i
	)	{
	Biss	iss	(*i);
	Str	s,v;

	if (!(iss>>s))
		throw "hissy fit"
	;
	s.replace(s.find(":"),1,"");

	if (!(iss>>v))
		throw "up"
	;
	m[s] = v;
 }
}

const Str &
Bloater::operator[](
	const Str & idx
)	const
{MSS::const_iterator	i	(m.find(idx));

 if (m.end() == i)
	throw "no such var"
 ;
 return i->second;
}

Bos &
Bloater::Emit(
	Bos	&	os
)	const
{static const char *emit[]	={
	"MemTotal",
	"MemFree",
	"MemAvailable",
	"Cached",
	"HardwareCorrupted",
	0
	}
 ;
 static Siz	pad	=20;
 const char	**p	=emit;

 for (p = emit; *p; p++)	{
	MSS::const_iterator	i	(m.find(*p));

	if (m.end() == i)
		continue
	;

	Str	s	(i->first);

	s.append(pad-s.length(),' ');
	os << s << i->second << '\n';
 }

 return os;
}

void
Main(void)
{ConStr	cs	(Load("/proc/meminfo"));
// DumpCon(cs);

 Bloater	b(cs);

 std::cout << b << std::endl;
}

int
main()
{
 try	{
	for (;;)	{
		std::cout << "\033[2J\033[H";
		Main();
		usleep(1000*1000);
	}
 }
 catch (...)	{
	std::cerr << "bugger!\n";
	return EXIT_FAILURE;
 }
 return EXIT_SUCCESS;
}
Add "-std=c++14" to the 'g++' flags. Adjust that static "emit[]" in Bloater::Emit() for which fields you want displaying. This should be about as slow as you can get 'cos it reads the whole of /proc/meminfo into an array then shoves it into a map which it then searches!

jahboater
Posts: 4605
Joined: Wed Feb 04, 2015 6:38 pm

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 10:42 pm

bedtime wrote:
Mon Mar 11, 2019 7:53 pm

Code: Select all

    do{
    }while(1); // '1' is better?
Sorry that was just a quick comment.
If there is no test then it doesn't matter what kind of while loop is used!
If there is a test, then a do while has one jump and a normal while has two jumps (jumps are bad).
And "for(;;)", "while(true)", and "while(1)" will all produce identical code.
The compiler is much cleverer than you might think!

Heater
Posts: 13111
Joined: Tue Jul 17, 2012 3:02 pm

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 10:50 pm

Is it useful to be thinking about saving an instruction or two in a loop construct that only goes around a handful of times when you are using cout to output the data?

jahboater
Posts: 4605
Joined: Wed Feb 04, 2015 6:38 pm

Re: C/C++ Memory Usage Code

Mon Mar 11, 2019 10:57 pm

Heater wrote:
Mon Mar 11, 2019 10:50 pm
Is it useful to be thinking about saving an instruction or two in a loop construct that only goes around a handful of times when you are using cout to output the data?
No - that's right.
That's what I call a "micro-optimization".

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Tue Mar 12, 2019 5:41 pm

swampdog wrote:
Mon Mar 11, 2019 10:32 pm
Well, as I had some unexpected free time today I thought I'd try the bloaty approach. This is because I watched a youtube vid last night about old games - Dungeon Keeper & Theme Hospital so I've had "bloaty head syndrome" wandering around my mind accompanied by squeaking imp sounds ever since I woke up, plus I wanted to play with that "auto operator<<" template. My exception handling is somewhat frivolous but its existence completes the kitchen sink.. ;-)

Code: Select all

#include <iostream>
#include <fstream>
#include <string>
...
Add "-std=c++14" to the 'g++' flags. Adjust that static "emit[]" in Bloater::Emit() for which fields you want displaying. This should be about as slow as you can get 'cos it reads the whole of /proc/meminfo into an array then shoves it into a map which it then searches!
I ran the code, and it worked fine, but, yeah, it was a trifle cpu intensive, taking about .25 - %1. Nonetheless, you certainly seem to know your C++!
Is it useful to be thinking about saving an instruction or two in a loop construct that only goes around a handful of times when you are using cout to output the data?
The cout was only for the forum, so you guys could easily test the code for yourselves. There is no cout at all in the code it's used in; the output is sent to xsetroot through a C++ X11 library and then grabbed by Dwm to be thrown in the status bar.
No - that's right.
That's what I call a "micro-optimization".
More like 'nano'-optimization. :D

I want to make sure that when I do things, that I understand what is happening. It is a small thing, but I want to start off on a good foot.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Sat Mar 16, 2019 6:55 pm

Perhaps this thread has been beaten more badly than Mikolka's horse in Crime and Punishment, but, I'd really like to share these last two optimisations, as I think they're worthy of it.

This new code is about ~20% more efficient than the above. And the C code is approx. 6% more efficient than the C++. They both require only one library to compile (so long as you don't use the 'sleep/usleep' functions, which bring in the 'unistd.h' library).

mem.c:

Code: Select all

/*

  // -O3 seems to give ~.6% increased efficiency (under 50% cpu load)
  g++ -Wall -O3 mem.c -o mem

*/

#include<fstream>
#include<unistd.h>   // Needed for 'usleep' func.

unsigned int mem()
{

    unsigned int memTotal, memFree, buffers, cached;

    FILE * const file = fopen( "/proc/meminfo", "r" );

    fscanf(file, "%*19s %i %*2s %*19s %i %*2s %*[^\n]\n %*19s %i %*2s %*19s %i", &memTotal, &memFree, &buffers, &cached);

    fclose(file);

    return ((memTotal - memFree) - (buffers + cached)) / 1024;
}

int main()
{
    
    do{

        printf("mem: %im\n", mem());
        sleep(1);

//      For testing:

//      mem();
//      usleep(55);

    }while(1);

    return 0;
}
mem.cpp

Code: Select all

/*
  // -O3 seems to be ~.6% more efficient under a 47% cpu load
  g++ -Wall -O3 mem.cpp -o mem 

*/

#include<fstream>
#include<unistd.h>   // Needed for usleep func.

int mem()
{

    unsigned int memTotal, memFree, buffers, cached;
    char id[18], kb[2];

    std::ifstream file("/proc/meminfo");
 
    file >> id >> memTotal >> kb \
         >> id >> memFree  >> kb \
         >> id >> buffers  >> kb \
         >> id >> buffers  >> kb \
         >> id >> cached   >> kb;

    file.close();

    return ((memTotal - memFree) - (buffers + cached)) / 1024;
}

int main()
{
    
    do{

        printf("mem: %im\n", mem());
        sleep(1);

//      For testing:

//      mem();
//      usleep(55);

    }while(1);

    return 0;
}
Still looking for suggestions! :)

swampdog
Posts: 230
Joined: Fri Dec 04, 2015 11:22 am

Re: C/C++ Memory Usage Code

Mon Apr 01, 2019 5:02 pm

You could use fseek(file,0,SEEK_SET) rather than keep reopening the file and "/1024" is the same as ">> 10" though it wouldn't surprise me if gcc worked out the latter for itself.

Yeah. I'm an erratic poster!

togomodo
Posts: 3
Joined: Mon Apr 01, 2019 4:48 pm

Re: C/C++ Memory Usage Code

Mon Apr 01, 2019 5:38 pm

you can use POSIX getrusage

#include <sys/resource.h>
#include <sys/time.h>
//above libraries have to be included

int getrusage(int who, struct rusage *usage);

Read the man page to find out how it works.

long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */

These are the 2 elements of the struct you should be looking for.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Wed Apr 03, 2019 7:53 pm

swampdog wrote:
Mon Apr 01, 2019 5:02 pm
You could use fseek(file,0,SEEK_SET) rather than keep reopening the file and "/1024" is the same as ">> 10" though it wouldn't surprise me if gcc worked out the latter for itself.

Yeah. I'm an erratic poster!
Great advice had the files not been proc files, but since they are (pseudo) proc files, they must be reopened each time to prompt the kernel for new values.

togomodo, Good idea, but I've tried this before, and it was much slower.

I did manage to get an increase of speed through the use of 'fgets':

mem.c:

Code: Select all

/*

  // -O3 seems to give .6% increased efficiency
  gcc -Wall -O3 mem.c -o mem

  43.0% CPU usage @ usleep(55)

*/

#include<unistd.h>   // Needed for 'usleep' func.
#include<stdio.h>

void naive_pos(const char *p, unsigned int *x, unsigned int pos)
{
    *x = 0;

    // bring position 'near' the number (number of digits can change!)
    for(; --pos > 1;)
       *++p;

    // find number's starting position
    while(*++p < '0' || *p > '9' );

    // the number is here, so extract it
    do{
        *x = (*x*10) + (*p - '0');
    }while(*++p > '/' && *p < ':' );
}

unsigned int mem()
{
    unsigned int memTotal, memFree, buffers, cached;
    char str [30];

    FILE * const file = fopen( "/proc/meminfo", "r");

    // Use fgets to quickly grab data
    fgets(str, 30, file);
    naive_pos(str, &memTotal, 19);
    fgets(str, 30, file);
    naive_pos(str, &memFree, 19);
    fgets(str, 30, file);            // line not used
    fgets(str, 30, file);
    naive_pos(str, &buffers, 19);
    fgets(str, 30 , file);
    naive_pos(str, &cached, 19);

    fclose(file);
    return ((memTotal - memFree) - (buffers + cached)) / 1024;
}

int main()
{

    do{

        // Everyday usage:
        printf("mem: %im\n", mem());
        sleep(1);

        // For testing:
        //mem();

        usleep(55);

    }while(1);

    return 0;
}

jahboater
Posts: 4605
Joined: Wed Feb 04, 2015 6:38 pm

Re: C/C++ Memory Usage Code

Thu Apr 04, 2019 9:02 am

Just out of interest, why do you sleep for 55 microseconds?
1 second or longer is usually fine enough granularity and then you wouldn't have to micro optimize the code.

As a tiny optimization, I would change:

void naive_pos(const char *p, unsigned int *x, unsigned int pos)

into:

static unsigned int naive_pos(const char *p, unsigned int x, unsigned int pos)

Just use x as before (without the stars) and return x at the end.
This may help because ...
1) Its always best to keep variables in registers, and taking the address of a variable (with &) forces the compiler to (at least temporarily) store it in memory.
2) The function becomes "pure" which opens up the possibility of other optimizations.
The const qualifier you gave "p" is a great help.
You could even add "__attribute__ ((pure))" to tell the compiler explicitly that it is pure.
3) static tells the compiler the function is never used anywhere else -fwhole-program would do the same thing.

swampdog
Posts: 230
Joined: Fri Dec 04, 2015 11:22 am

Re: C/C++ Memory Usage Code

Thu Apr 04, 2019 3:48 pm

I simply googled and went from this..
https://stackoverflow.com/questions/19 ... udo-files
..rather than personal knowledge of how the kernel acts in that respect. However, as you're into overly excessive optimisation ;-)

It "works" on my rpi so it's fair game. It's possible to go further and (imo) the code below is taking it too far. No error checking (assumption /proc/meminfo never fails) and reusing pre-calculated pointers from an initial read (assumption buffer sits at 1008 chars and assumption those spaces used for padding don't suddenly change into tabs). Fwiw..

Code: Select all

#include <unistd.h>
#include <string.h>
#include <stdio.h>

static char x[1024*10]; /* 1008 chars */
/* hexdump /proc/meminfo and we see it pads with spaces so the size is not
   going to change thus we could read it once, malloc 'x' and reuse the
   buffer. Can't be bothered but we do reuse the buffer.
*/

static inline
size_t
GetVal(const char s[])
/* skips the leading spaces and stops after the value.
*/
{
 return strtoul(s,NULL,10);
}

static inline
void
Init(const char *vp[], const char buf[], const char *var[])
/* set the pointers just the once because the offsets are not going to
   change.
*/
{size_t	i;

 for	(
	i = 0;
	var[i];
	i++
	)
	vp[i] = strstr(buf,var[i]) + strlen(var[i])
 ;	
}

static inline
void
Emit(const char **varp, const char**valp)
{
 while (NULL != *varp)
	printf("%s %lu\n",*varp++,GetVal(*valp++))
 ;
}

int
main()
/* all error checking omitted!
*/
{const char 	fn[]	="/proc/meminfo";
 int		fd	=open(fn,0);
 const char	*var[]	={
	"MemTotal:",
	"MemFree:",
	"Buffers:",
	"Cached:",
	0
 };
 const char	*vp[]	={
	0,0,0,0,
	0
 };

 /* fill buffer and set pointers to variables */
 read(fd,x,1024*10);
 Init(vp,x,var);
 Emit(var,vp);
 sleep(1);

 /* just reload buffer and reuse initial pointer values */
 do	{
	lseek(fd,0,SEEK_SET);
	read(fd,x,1024*10);
	Emit(var,vp);
	sleep(1);
 }while(0);/* 1 */

 close(fd);
 return 0;
}
It's not too much work dispensing with "stdio.h" completely.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Sun Apr 14, 2019 10:53 pm

Sorry for the late reply. I didn't know there was responses until this morning...
jahboater wrote:
Thu Apr 04, 2019 9:02 am
Just out of interest, why do you sleep for 55 microseconds?
1 second or longer is usually fine enough granularity and then you wouldn't have to micro optimize the code.
The microsecond sleep is for testing CPU usage.
As a tiny optimization, I would change:

void naive_pos(const char *p, unsigned int *x, unsigned int pos)

into:

static unsigned int naive_pos(const char *p, unsigned int x, unsigned int pos)

Just use x as before (without the stars) and return x at the end.
This may help because ...
1) Its always best to keep variables in registers, and taking the address of a variable (with &) forces the compiler to (at least temporarily) store it in memory.
2) The function becomes "pure" which opens up the possibility of other optimizations.
The const qualifier you gave "p" is a great help.
You could even add "__attribute__ ((pure))" to tell the compiler explicitly that it is pure.
3) static tells the compiler the function is never used anywhere else -fwhole-program would do the same thing.
I'm going to have to do some reading to absorb this. I'm have a lot to learn in, C but i will look it up. Thanks. :)

swampdog,

Amazing coding there! I like the idea of grabbing the info's location the one time and reusing it. This has reduced the CPU from 47% to 32%. Your code has given me ideas for other /proc-related code. :)

I quickly switched some things around and made somewhat of a replica. I still need to fix some things, but I thought I'd get this out, as you guys have been waiting long enough.

mem.c:

Code: Select all

/*

 gcc -Wall -O3 mem.c -o mem

*/

#include <unistd.h> // u/sleep()
#include <string.h> // strstr
#include <stdio.h>  // printf
#include <fcntl.h>  // open 

static char mem_buff[256]; // Just take what's needed

inline size_t fast_strlen(const char *str)
{
    int i;   // Cannot return 'i' if inside for loop
    for(i = 0; str[i] != '\0'; ++i);

    return i;
}

static inline void mem_init(const char *meminfo[], const char buf[])
{
    const char *var[]  = {
        "MemTotal:",
        "MemFree:",
        "Buffers:",
        "Cached:",
        0
    };

    for	(size_t i = 0; var[i];)
    {
        // TODO: work on making own faster strstr func.
	meminfo[i] = strstr(buf, var[i]) + fast_strlen(var[i]);

        ++i;
    }
}

static inline unsigned long fast_strtoul(const char *c)
{   
    int x = 0;

    // move pointer to first number
    for(; *c < 48 || *c > 57; )
        c++;
   
    // we are on a number, so convert it 
    for(; *c > 47 && *c < 58;)
    {   
        // shifting more efficient than multiplication
        x = (x << 1) + (x << 3) + *c - 48;

        c++;
    }
    
    return x;
}

static inline unsigned long mem(int * fd, const char **valp)
{

    lseek(*fd, 0, SEEK_SET);
    read(*fd, mem_buff, 256);

    return ((fast_strtoul(*valp) - fast_strtoul(*(valp + 1) ))
          - (fast_strtoul(*(valp + 2)) + fast_strtoul(*(valp + 3)))) / 1024; 

}

int main()
{
    int mem_file          = open("/proc/meminfo", 0);
    const char *meminfo[] = {0, 0, 0, 0, 0};

    read(mem_file, mem_buff, 256);
    mem_init(meminfo, mem_buff);

    do{

        // testing
	mem(&mem_file, meminfo); usleep(55);

        // everyday usage
        printf("Mem: %lu\n", mem(&mem_file, meminfo)); sleep(1);

    }while(1);

    close(mem_file);
    return 0;
}

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Tue Apr 16, 2019 3:21 am

I've been working on a system monitor and had I been paying attention to my threads I would have seen this sooner.

Memory usage is easy to get you use

Code: Select all

#include <sys/sysinfo.h>
Here is the struct it defines

Code: Select all

struct sysinfo {
               long uptime;             /* Seconds since boot */
               unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
               unsigned long totalram;  /* Total usable main memory size */
               unsigned long freeram;   /* Available memory size */
               unsigned long sharedram; /* Amount of shared memory */
               unsigned long bufferram; /* Memory used by buffers */
               unsigned long totalswap; /* Total swap space size */
               unsigned long freeswap;  /* Swap space still available */
               unsigned short procs;    /* Number of current processes */
               char _f[22];             /* Pads structure to 64 bytes */
           };
and you get your information like so

Code: Select all

struct sysinfo  s_info;
if ( 0 ==  sysinfo(&s_info) )
{
   // s_info is good
}
I'm using my phone to write this post I hope this information is helpful to you.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Wed Apr 17, 2019 10:10 am

DarkElvenAngel wrote:
Tue Apr 16, 2019 3:21 am
I've been working on a system monitor and had I been paying attention to my threads I would have seen this sooner.

Memory usage is easy to get you use

Code: Select all

#include <sys/sysinfo.h>
Here is the struct it defines

Code: Select all

struct sysinfo {
               long uptime;             /* Seconds since boot */
               unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
               unsigned long totalram;  /* Total usable main memory size */
               unsigned long freeram;   /* Available memory size */
               unsigned long sharedram; /* Amount of shared memory */
               unsigned long bufferram; /* Memory used by buffers */
               unsigned long totalswap; /* Total swap space size */
               unsigned long freeswap;  /* Swap space still available */
               unsigned short procs;    /* Number of current processes */
               char _f[22];             /* Pads structure to 64 bytes */
           };
and you get your information like so

Code: Select all

struct sysinfo  s_info;
if ( 0 ==  sysinfo(&s_info) )
{
   // s_info is good
}
I'm using my phone to write this post I hope this information is helpful to you.
For ease of use and production speed, it's a great function; however, in my tests it was much slower at retrieving values, and it costs an extra library and memory usage.

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Wed Apr 17, 2019 11:37 am

bedtime wrote:
Wed Apr 17, 2019 10:10 am
For ease of use and production speed, it's a great function; however, in my tests it was much slower at retrieving values, and it costs an extra library and memory usage.
Do you have numbers on that I'm interested if I can trim down my system monitor to be more efficient right now pulling CPU data load, averages, temperature, filesystem status. After all the data is read I preform a screen redraw.

All use about .4% memory and at check time .7% cpu load, and 0 load for wait times That's reported from htop.

These test are on a RPI 0. The system monitor uses ncurses so there's overhead for the library and the redraws.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Wed Apr 17, 2019 8:00 pm

DarkElvenAngel wrote:
Wed Apr 17, 2019 11:37 am
bedtime wrote:
Wed Apr 17, 2019 10:10 am
For ease of use and production speed, it's a great function; however, in my tests it was much slower at retrieving values, and it costs an extra library and memory usage.
Do you have numbers on that I'm interested if I can trim down my system monitor to be more efficient right now pulling CPU data load, averages, temperature, filesystem status. After all the data is read I preform a screen redraw.

All use about .4% memory and at check time .7% cpu load, and 0 load for wait times That's reported from htop.

These test are on a RPI 0. The system monitor uses ncurses so there's overhead for the library and the redraws.
I don't have the data, sorry; I just remember that it was slow enough to make it not even an option for me. In any event, I will post my github link for you to peruse and to save myself time:

https://github.com/bathtime/all/blob/master/bar.c

It's not exactly made for anyone but myself, and I'm not a professional, so be warned. The status bar program has threading for additional background programs to be seamlessly loaded and displayed as if in realtime. I use a thread to run, fetch, and display the results from my weather app, for example.

Would you be willing to post your code? Your specs seem quite good considering it is run on a Pi 0.

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Wed Apr 17, 2019 10:49 pm

I can post my code but I'll have to modify it first so it will compile and run as I have used hard coded links for my framework that will not be present. As well as a custom library.

I tried posting screenshots but they didn't work properly. I'll have time this weekend to post the code.

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Thu Apr 18, 2019 1:26 am

sysmon2.png
sysmon2.png (32.96 KiB) Viewed 1723 times
sysmon1.png
sysmon1.png (37.49 KiB) Viewed 1723 times
Screen shots from lxTerminal connected over ssh.

I was going to post the unedited mess of my system monitor's cpustat.c, But it looks like I was not as concerned with comments, or naming conventions as I am now. I'd only started learning C last year for use on the raspberry pi, and this was one of the first programs I tried to port from python to C. This is also the first time I learned how to break C program into multiple files and use headers, so it's just a mess.

I just looked at the source on my personal git server and it would not be helpful to anyone except maybe as a lesson in what not to do.

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Fri Apr 19, 2019 7:32 pm

DarkElvenAngel wrote:
Thu Apr 18, 2019 1:26 am
sysmon2.png

sysmon1.png

Screen shots from lxTerminal connected over ssh.

I was going to post the unedited mess of my system monitor's cpustat.c, But it looks like I was not as concerned with comments, or naming conventions as I am now. I'd only started learning C last year for use on the raspberry pi, and this was one of the first programs I tried to port from python to C. This is also the first time I learned how to break C program into multiple files and use headers, so it's just a mess.

I just looked at the source on my personal git server and it would not be helpful to anyone except maybe as a lesson in what not to do.
That's okay. I often feel that way about my code as well. Thanks for posting the pics. Looks like you put a lot of work into that code.

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Mon Apr 22, 2019 4:01 am

Okay he is the code.
This code on its own will not do anything it is part of my GUI project. (I forgot I copied it from my system monitor because I needed to see how much CPU I was using.)

since I wanted to share something that works I stripped out all the functions that weren't needed to get it going.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <limits.h>
#include <pthread.h>


pthread_t 			CPU_tid;
char 				CPU_tctrl = 0;
int 				CPU_Load = 255;
int 				CPU_Temp = 255;
int				mem_total = -1;
int				mem_used= -1;
// Thread Prototypes

void *ThreadCPU(void *vargp)
{
	fprintf (stderr,  "SYSMON: ThreadCPU Running\n");
	char str[101];
	const char d[2] = " ";
	char* token;
	int i = 0;
	unsigned long int sum = 0, idle = 0, lastSum = 0,lastIdle = 0;
    int CPUTemp;
    char PrevMode = CPU_tctrl;
    FILE* fp;
    FILE* fptemp;
    struct sysinfo s_info;
    while (CPU_tctrl < 254)    // loop til thread control says terminate
	{
        if (PrevMode != CPU_tctrl)
        {
			fprintf (stderr, "SYSMON: Thread mode changed from 0x%02x to 0x%02x\n", PrevMode, CPU_tctrl);
			PrevMode = CPU_tctrl;
		}
        switch (CPU_tctrl)
        {
            case 0:  // WAIT
				break;
	    case 1:  // INIT
				sleep(1);
				CPU_tctrl = 2;
				break;
            case 2: // RUN
				//times++;
				i = 0;
				fp = fopen("/proc/stat","r");
				fgets(str,100,fp);
				fptemp = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
				fscanf(fptemp, "%d", &CPUTemp);
				fclose(fptemp);
				fclose(fp);
				token = strtok(str,d);

				while(token!=NULL)
				{
					token = strtok(NULL,d);
					if(token!=NULL)
					{
						sum += atoi(token);
						if(i==3)
							idle = atoi(token);
						i++;
					}
				}
				CPU_Temp = CPUTemp / 1000;
				CPU_Load = (1000*((sum-lastSum)-(idle-lastIdle))/(sum-lastSum)+5)/10;
				lastIdle = idle;
				idle=0;
				lastSum = sum;
				sum=0;
      				if ( 0 ==  sysinfo(&s_info) )
			      	{
        				mem_total = s_info.totalram /1024 /1024; 
					mem_used = (s_info.totalram - s_info.freeram) /1024 /1024;
      				}

			break;
		case 254:  // PRE-CLOSE THREAD
				CPU_tctrl = 255;
			break;
        }
        sleep(1);
    }
    CPU_tctrl = 255;
    return NULL;
}


// Control Functions

void InitSysMon()
{

	int error = pthread_create(&CPU_tid, NULL, &ThreadCPU, (void *)&CPU_tid);
	if (error != 0)
	{
		fprintf(stderr, "SYSMON: Error Can't create thread (Error: %d)\n", error);
	}
}

void CloseSysMon()
{
	if (CPU_tctrl != 255)
	{
		CPU_tctrl = 254;
		fprintf (stderr,  "SYSMON: Thread Close\n");
	} else {
		fprintf (stderr,  "SYSMON: Thread already dead\n");
	}
}
I think it's straight forward the idea is to thread the system monitor and read the results from the global variables, I've tested the code in a daemon I'm toying with. The performance is 0.3% mem and 0.7% CPU every update cycle idle is 0.

The daemon has no other tasks it just waits for an IPC message to log or command a shutdown so that's not affecting these values as much as ncurses would have.

To compile this requires only libpthread

bedtime
Posts: 67
Joined: Thu Dec 13, 2018 6:02 pm

Re: C/C++ Memory Usage Code

Wed Apr 24, 2019 5:33 pm

DarkElvenAngel wrote:
Mon Apr 22, 2019 4:01 am
Okay he is the code.
This code on its own will not do anything it is part of my GUI project. (I forgot I copied it from my system monitor because I needed to see how much CPU I was using.)

since I wanted to share something that works I stripped out all the functions that weren't needed to get it going.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <limits.h>
#include <pthread.h>


pthread_t 			CPU_tid;
char 				CPU_tctrl = 0;
int 				CPU_Load = 255;
int 				CPU_Temp = 255;
int				mem_total = -1;
int				mem_used= -1;
// Thread Prototypes

void *ThreadCPU(void *vargp)
{
	fprintf (stderr,  "SYSMON: ThreadCPU Running\n");
	char str[101];
	const char d[2] = " ";
	char* token;
	int i = 0;
	unsigned long int sum = 0, idle = 0, lastSum = 0,lastIdle = 0;
    int CPUTemp;
    char PrevMode = CPU_tctrl;
    FILE* fp;
    FILE* fptemp;
    struct sysinfo s_info;
    while (CPU_tctrl < 254)    // loop til thread control says terminate
	{
        if (PrevMode != CPU_tctrl)
        {
			fprintf (stderr, "SYSMON: Thread mode changed from 0x%02x to 0x%02x\n", PrevMode, CPU_tctrl);
			PrevMode = CPU_tctrl;
		}
        switch (CPU_tctrl)
        {
            case 0:  // WAIT
				break;
	    case 1:  // INIT
				sleep(1);
				CPU_tctrl = 2;
				break;
            case 2: // RUN
				//times++;
				i = 0;
				fp = fopen("/proc/stat","r");
				fgets(str,100,fp);
				fptemp = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
				fscanf(fptemp, "%d", &CPUTemp);
				fclose(fptemp);
				fclose(fp);
				token = strtok(str,d);

				while(token!=NULL)
				{
					token = strtok(NULL,d);
					if(token!=NULL)
					{
						sum += atoi(token);
						if(i==3)
							idle = atoi(token);
						i++;
					}
				}
				CPU_Temp = CPUTemp / 1000;
				CPU_Load = (1000*((sum-lastSum)-(idle-lastIdle))/(sum-lastSum)+5)/10;
				lastIdle = idle;
				idle=0;
				lastSum = sum;
				sum=0;
      				if ( 0 ==  sysinfo(&s_info) )
			      	{
        				mem_total = s_info.totalram /1024 /1024; 
					mem_used = (s_info.totalram - s_info.freeram) /1024 /1024;
      				}

			break;
		case 254:  // PRE-CLOSE THREAD
				CPU_tctrl = 255;
			break;
        }
        sleep(1);
    }
    CPU_tctrl = 255;
    return NULL;
}


// Control Functions

void InitSysMon()
{

	int error = pthread_create(&CPU_tid, NULL, &ThreadCPU, (void *)&CPU_tid);
	if (error != 0)
	{
		fprintf(stderr, "SYSMON: Error Can't create thread (Error: %d)\n", error);
	}
}

void CloseSysMon()
{
	if (CPU_tctrl != 255)
	{
		CPU_tctrl = 254;
		fprintf (stderr,  "SYSMON: Thread Close\n");
	} else {
		fprintf (stderr,  "SYSMON: Thread already dead\n");
	}
}
I think it's straight forward the idea is to thread the system monitor and read the results from the global variables, I've tested the code in a daemon I'm toying with. The performance is 0.3% mem and 0.7% CPU every update cycle idle is 0.

The daemon has no other tasks it just waits for an IPC message to log or command a shutdown so that's not affecting these values as much as ncurses would have.

To compile this requires only libpthread
Where is main()? If you add it I'll give it a spin. :)

DarkElvenAngel
Posts: 152
Joined: Tue Mar 20, 2018 9:53 pm

Re: C/C++ Memory Usage Code

Thu Apr 25, 2019 2:49 am

bedtime wrote:
Wed Apr 24, 2019 5:33 pm
Where is main()? If you add it I'll give it a spin. :)
If you want to see some output try this with it.

Code: Select all

#include <stdio.h>
#include <unistd.h>

void InitSysMon();
void CloseSysMon();

extern char CPU_tctrl;
extern int CPU_Load;
extern int CPU_Temp;
extern int mem_total;
extern int mem_used;

int main()
{
  InitSysMon(); // launch sysmon thread
  CPU_tctrl = 2; // set thread to run mode
  printf("\e[?25l"); // turn off cursor
  sleep (2); // give thread time to initialize
  for (int x = 0; x < 10; x++)
  {
    printf("CPU:  %03d%%  %2dC\tMEM:   %3dMb free\r", CPU_Load, CPU_Temp, (mem_total - mem_used));
    fflush(stdout); // send text to stdout now
    sleep (1); // wait for new data
  }
  printf("\e[?25h\n"); // turn cursor on
  CloseSysMon();  // shutdown thread
}
save this code as main.c and the previous code as sysmon.c

then to compile execute

Code: Select all

gcc main.c sysmon.c -lpthread
That's a basic test and you can see how it's portable. The thread can be modified to signal when it's got data or fire off a callback. I opted for a simpler loop and global variables to demonstrate the code so nothing needs changing.

You could use a while loop in place of the sleep(2); and just watch for one of the variable to go into bounds that's what I do in my GUI the system information is hidden until the values are valid.

Return to “C/C++”