Idling with engine on / idling with engine off

busy waiting vs sleeping/blocking

There are two ways time can pass while a process places a hold on doing anything significant. One is that it keep busy doing something insignificant, so that it can't do something significant. The other is that it keep from doing anything at all, so that it can't do something significant. The program in both cases is seen to be "waiting." However from the computer's point of view, not the program's, there's a big difference. In the first case the program is doing a lot. And that costs money! Not literally, but it does burn CPU cycles, and electricity. So perhaps even money. In the second case the program is on ice, held away from the CPU so costs no cycles at all.

Remember that programs run only on the initiative of the operating system, more specifically its "job scheduler" component. There are typically numerous processes concurrently running at any given time. The job scheduler circulates among them in some fashion, pushing one then the other forward to the CPU for a short interval of activity. So the OS is the boss. It decides when each process gets to run. Being the boss, it can also decide whether the process gets to run. If the process is supposed to get its turn once in a while, the OS could decide to skip its turn. It could do this for a long time. The process remains "on the books" but is not participating. 

Below is a pair of programs. They both do the same thing. Namely, cause themselves to pass a certain amount of time and then terminate. But they do it differently. busy-timewaster.c does it by running a time-consuming loop ( for{for{for{...}}} ). sleeping-timewaster.c does it by asking the operating system to give it a timed-dose knockout injection ( sleep( ) ).

busy-timewaster.c - process goes through motions

sleeping-timewaster.c - OS doesn't schedule process

Source code:

/* consumes time, looping around in circles,
* proportional (exponentially) to value of "count"
*/
#include <stdio.h>
main(int argc, char *argv[])
{ int i,j,k,count;

if(argc<2)
{
 printf("\nPlease type:");
 printf("\n busy-timewaster <count>");
 printf("\n e.g. busy-timewaster 10");
 printf("\n higher values of count make the program take longer\n\n");
 return -1;
}

count=atoi(argv[1]);

printf( "\nstart\n" );
for (i=0;i<count;i++) {
 for (j=0;j<count;j++) {
  for (k=0;k<count;k++) { }}}
   printf("finish\n\n");
}

 

Source code:

/* consumes time, by having OS put it to sleep */
#include <stdio.h>
main(int argc, char *argv[ ]) {
int duration;

if(argc<2)
{
 printf("\nPlease type:");
 printf("\n sleeping-timewaster <duration>");
 printf("\n e.g. sleeping-timewaster 10");
 printf("\n the program will sleep for <duration> seconds\n\n");
 return -1;
}

duration=atoi(argv[1]);

printf( "\nstart\n" );
sleep(duration);
printf("finish\n\n");
}

 


The exercise for you to perform

Obtain the programs, in the form of file timewasters.zip. (To get the file into your VM, there are descriptions of several file transfer methods. I like the shared folder approach.)

After you get the file unzip it:

unzip  timewasters.zip

It contains 3 programs. Compile them all:

gcc  busy-timewaster.c  -o  busy-timewaster
gcc  sleeping-timewaster.c  -o  sleeping-timewaster
gcc  combo-timewaster.c  -o  combo-timewaster

Run busy-timewaster:

./busy-timewaster

It will print a syntax admonishment letting you know it needs some number as an argument. So, try 1000:

./busy-timewaster  1000

How long it takes will vary a lot among machines. Find out how long it takes on your machine:

time  ./busy-timewaster  1000   see footnote below*

Check the reported "real" or elapsed time. Now vary the number till the program takes about 5 seconds.

time  ./busy-timewaster  1310

If your machine is faster or slower than mine, your number will be larger or smaller than 1310.

Now run sleeping-timewaster:

./sleeping-timewaster

Another admonishment. Please follow the admonishment's rules, tell it to waste 5 seconds:

./sleeping-timewaster  5

Now time it:

time  ./sleeping-timewaster  5

Run both of these commands, the "busy" and "sleeping" versions, each of which takes 5 seconds then quits. From the outside perspective they do the same thing.

time  ./busy-timewaster  1310    [ substitute your 5-second number for my 1310 ]
time  ./sleeping-timewaster  5

But look at the values for "user" time. That means the time the process held control of the cpu and was actively executing its code, to the exclusion of other processes. For "busy" it's about the whole 5 seconds; for "sleeping" it's about zero. Which one is more taxing on the system, and which one is lighter on it?

Below is the third program, combo-timewaster, which just combines the other two. Their core logic appears inside a loop that runs 10 times, alternating busy and sleeping execution. So you could tune it so that the program waits busy for 5 seconds, then waits idle for another 5 seconds. combo-timewaster would repeat that 10 times. While it does so, let's run another program on the side that shows system resource usage. The command line utility "top" is such a program. Watch system usage (specifically, CPU time) spike up and fall back down again and again as combo-timewaster runs. A good way to do it is with a split screen, one showing combo-timewaster running and the other running top showing combo-timewaster's resource consumption. A program called terminator can give you a split screen. Run it full-screen:

terminator -f

In the terminal window right mouse click then choose "Split Horizontally" from the resulting menu. You now have two windows, in which you can run two commands independently. Move back and forth between them with the ctrl-Tab key combination or by mouse clicking in the one you wish to select. In the upper one, launch combo-timewaster:

./combo-timewaster  1310  5

Then switch to the lower one and have top dynamically display the cpu consumption of combo-timewaster as it rises and falls:

top -d 0.5 -p $(pidof combo-timewaster)

Exit from the top command with the q keystroke. Exit from these windows with the "exit" commnd

/* consumes time, looping around in circles,
* proportional (exponentially) to value of "count"
*/
#include <stdio.h>
main(int argc, char *argv[])
{ int i,j,k,h,count,duration;

if(argc<3)
{
 printf("\nPlease type:");
 printf("\n busy-timewaster <count> <seconds to sleep>");
 printf("\n e.g. busy-timewaster 1000 5");
 printf("\n higher values of count make the program take longer\n\n");
 return -1;
}

// alternate between waiting "busy" and waiting "idle"
count=atoi(argv[1]);
duration=atoi(argv[2]);
for (h=0;h<10;h++) {
printf( "\nstart being busy\n" );
for (i=0;i<count;i++) {
 for (j=0;j<count;j++) {
  for (k=0;k<count;k++) { }}}
   printf("finish being busy\n\n");


printf( "\nstart sleeping\n" );
sleep(duration);
printf("finish sleeping\n\n");
}
}


* time is a command that stopwatches a process, measuring the elapsed real time it takes to complete. It can break down that time interval into a subinterval portion during which the process's own code was executing in the CPU, and another sub-portion during which code belonging to the kernel was doing so on the process's behalf. Any remaining time was spent by the CPU on other business than this process. For example, if a process is timed to have taken 10 seconds to complete with 4 seconds devoted to its own code and 1/2 second to kernel code doing process-related administrative business, then the CPU spent the other 5 1/2 seconds on business unrelated to this program. Note there are two different "time" programs. One is a bash shell built-in, and is the one you get by default. The other is a regular, stand-alone utility program usually found in /usr/bin/time. The above program uses the built-in. Try invoking the other one, as follows, for a slightly different form of output and ability to control it:

/usr/bin/time -f "%U user\n%S system\n%E elapsed\n%P percentage" ./sleeping-timewaster