Measuring the energy consumed by a command using the Intel® Energy Checker SDK

In my first blog entry, I showed how simple it was to improve the accuracy of the power draw reported by the Intel® Energy Checker SDK’s stock ESRV simulated device library. I also opened-up for a nice research project consisting of using various system-level data to model more precisely a host system’s power draw. Data such as processor load, memory and I/O usage, P-State or C-State residency are good candidates to explore. In fine, the simulated power draw should be compared to the real power draw to check the model’s accuracy. In my second blog entry, I will therefore show how one can use the Intel® EC SDK API to craft a simple command-line utility to measure the energy consumed by a program. To achieve this goal, I will demonstrate in this blog entry the use of pl_attach(), pl_read() & pl_close() API functions and how to manage ESRV programmatically.

Our utility, named energy, will work à-la time command of UNIX*. If you are not acquainted with time, this command-line utility times a simple command and provides the elapsed (or real) time between the invocation of the utility and its termination; the user CPU time, equivalent to the sum of the tms_utime and tms_cutime fields returned by the times() function for the process in which the utility is executed; and finally, the system CPU time, equivalent to the sum of the tms_stime and tms_cstime fields returned by the times() function for the process in which the utility is executed. Certainly, different flavors of the time utility will provide more or less information. For example, the command time find ./src/ -name "*.c" prints on my Solaris* 10 system the following output:

real 0m0.024s
user 0m0.001s
sys 0m0.004s

Similarly, our energy utility will report the energy consumed between the invocation of the command provide by the user and its termination. This is slightly different from the time utility, since we do not want to factor-in the energy consumed during the utility’s setup and teardown. For sure, this behavior can be changed easily since the source code is provided (attached at the end of this blog entry). An example of the output generated by the energy utility is: Energy: [6016.03] Joule(s) - [0.00167112] kWh(s). Because there is no ways to measure the power consumed by a given process with today’s platforms – by sheer lack of suitable hardware hooks – only the energy consumed by the entire system is reported by our sample. This is the equivalent the elapsed (real) time returned by the time utility.
In addition of being useful as-is – at least I hope so :) – this sample will allow me to demonstrate two important tasks a programmer will have to perform when writing an energy aware application, the first step toward energy efficient software.
The first task is to drive programmatically the ESRV utility shipped with the SDK. Remember that ESRV is the SDK tool in charge of driving a power analyzer and sampling the power and energy readings. Even if it is simple to use ESRV manually, in some cases – like today – we may want to shield the user from this complexity.
The second task is to access and use the energy data provided by ESRV via its PL. This last task is detailed in-extenso in the esrv_client sample of the SDK (\iecsdk\src\samples\esrv_client). Since repetition is at the heart of the learning process, it will not hurt to show how it can be done once again.

The plan

In the grand scheme, our sample will either start a new instance of ESRV or will attach to an existing instance of the ESRV to measure the energy consumed. This behavior is based upon the user’s input. Whichever path is taken, once the energy utility has attached to the correct ESRV’s PL, it will run the command provided by the user via the command line interface. The energy counter ([CHANNELx] - Energy (Joule)) of the PL is read when the command begins to execute and when the command ends. The energy consumed while the command is run is computed as the difference of these two readings and is reported in Joules and in kWh (one kWh = 3,600,000 Joules). It is important to signal that these are the only times we read the energy counter of the PL! This really demonstrates the value and the elegance of the integral counters! I encourage you to read the following blog entry written by Kevin Bross for more on this topic. Once started, it is possible to interrupt the execution of the command by typing the <CTRL>+<C> key sequence. If this happens, then the end energy is read when the interrupt signal is processed by the signal handler of the sample.

Drive ESRV programmatically

For this sample, I decided to offer three different ways to specify which ESRV instance the energy utility has to attached to. This precision is required since multiple, live or dead, instances of ESRV may co-exist on a given system. As stated earlier, either a running ESRV must be used or a new instance of ESRV has to be started (and stopped gracefully at the end of our run) prior to running the command the user wants to measure. On one hand, if nothing is specified by the user regarding the ESRV instance to use then we will start and stop our own ESRV instance. On the other hand, if any of the following mutually exclusive options (--guid_shell_variable or --guid) is used then an existing instance of ESRV will be used. Note that these options are listed in order of suggested preference.
--guid_shell_variable followed by a shell environment variable name, assumes that the ESRV instance’s guid to use is stored in that variable. This scenario makes sense for example if the user or the administrator has started ESRV earlier and did set manually a variable with the guid returned by ESRV (for example in ESRV_GUID). It is also possible to set automatically such variable from a script at system startup. Have a look to the sample given in section 6.5 of the SDK user guide (scripting sample) for inspiration. When such variable exist, the energy utility will attach to the ESRV instance identified by the guid stored in it.
The second option in decreasing order of preference is --guid followed by a guid. With the exception of providing explicitly the guid of the ESRV instance to attach-to, this option is similar to the first one. In both cases, the validity of the guid – at least it’s form – is checked in the parser() function of the sample. The following code snippet demonstrates this:

p->esrv_guid_shell_variable = p->argv[i];
p->esrv_guid_shell_variable_value = getenv(p->esrv_guid_shell_variable);
if(p->esrv_guid_shell_variable_value == NULL) {
// error
} else {
ret = plh_filter_uuid_string(p->esrv_guid_shell_variable_value);
if(ret == PL_FAILURE) {
// error
}
}

If no ESRV instance is specified, then the energy utility will start its own instance of ESRV to measure the energy consumed. By default, it uses the default settings of ESRV (c.f. SDK and SDK device kit user guides for details). It is also possible to specify a non-default configuration to use with the new instance of ESRV. This option is --esrv_options followed by a valid options string (as described in the SDK user guide). Note that the debug version of the utility (built with _DEBUG or __PL_DEBUG__ symbol defined) uses the stock simulated library (esrv_simulated_device.dll). This demonstrates once again, that if you do not have access to a power analyzing device with your development environment, you can use the simulated device and still implement all the required energy centric functionalities into your code. Last but not least, it is possible to specify which ESRV channel to use via the --channel option. Indeed, ESRV can drive devices offering multiple measurement channels. By using this option, it is possible to overwrite the default channel (the first one) used for the measurements.
The energy utility invocation examples below show the three ways of specifying the ESRV instance to attach to. The suspension points (…) at the end of the examples are meant to indicate the fact that the commands are incomplete. Use the --help option to print detailed help.

energy --guid_shell_variable ESRV_GUID …
energy --guid eecae0bc-f7f4-4c9f-8373-d274c238d579 …
energy …
energy --esrv_options "--library yokogawa_wt500.dll –device_options 'items=all'" --channel 2 …

When the energy utility starts a new ESRV instance, then it creates a child process for it. It then uses the output of ESRV to read-in the guid. At the end of the command execution – or if the program is interrupted by the user – the energy utility kills the ESRV instance and removes the PL from the PL_FOLDER, leaving no traces of its run. Please refer to the sample code for coding details.

All the roads lead to pl_attach()

Whichever option is used, at the end of the day, the energy utility will need to attach to the PL fed with data by an ESRV instance. This operation is done simply by using the pl_attach() API function as shown in the code below.

//--------------------------------------------------------------
// Attach to the identified instance of ESRV and read settings.
//--------------------------------------------------------------
p->esrv_pld = pl_attach(esrv_config_file_name_buffer);
if(p->esrv_pld == PL_INVALID_DESCRIPTOR) {
// error
}

The esrv_pld integer is the PL descriptor returned by pl_attach(). This descriptor is then used by the pl_read() and pl_close() API functions. And this is precisely what the sample does right after. The first batch of pl_read() function calls are dedicated to perform few sanity checks:

    • Read the number of channels available in this PL (ESRV_COUNTER_CHANNELS_INDEX) and check that the requested one exists. This check is performed first since all the other counters are channel indexed – or specific if you prefer.

    • Read the status counter (ESRV_COUNTER_STATUS_INDEX) for the requested channel and check if it is associated with a running (live) ESRV instance.



After controlling these items, a set of configuration counters are read:

    • The precision of the energy counter (ESRV_COUNTER_ENERGY_JOULES_DECIMALS_INDEX). Read section 2.3.4 of the SDK user guide for details on suffix counters.

    • The version counter (ESRV_COUNTER_VERSION_INDEX). This counter is not used in the sample code. If future versions of ESRV are released then this counter could be used to check the availability of a given functionality.



If all these operations are carried-out successfully then the energy utility can start the command the user is interested in!

Measure the consumed energy

And it all starts with reading the value of the energy counter of the attached ESRV instance using the pl_read() function as shown below. Simple, isn’t it!

//-------------------------------------------------------------------------
// Read start energy.
//-------------------------------------------------------------------------
plret = pl_read(
p->esrv_pld,
&p->start_energy_data,
(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_ENERGY_JOULES_INDEX
);
if(plret != PL_SUCCESS) {
// error
}

Immediately after, the command is executed in a dedicated child process. Any output generated by the command is echoed on the standard output – with some cosmetic processing. This is not an issue in general, but you may want to remove it / change it if you are concerned by the energetic cost of this extra processing. The sample code points this out clearly so you can remove the echoing if you wish.
At the end of the command execution – or when the user pressed the <CTRL>+<C> key sequence – the energy counter is read again and the PL is closed. Nothing stellar.

//-------------------------------------------------------------------------
// Read end energy and close ESRV PL.
//-------------------------------------------------------------------------
plret = pl_read(
p->esrv_pld,
&p->end_energy_data,
(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_ENERGY_JOULES_INDEX
);
if(plret != PL_SUCCESS) {
// error
}
plret = pl_close(p->esrv_pld);
if(plret != PL_SUCCESS) {
// error
}


Joule or kWh? Why not both?

We now have all the data required (end_energy_data and start_energy_data) to report the energy consumed by the system while running the user’s command.

//-------------------------------------------------------------------------
// Compute energy consumed.
//-------------------------------------------------------------------------
p->consumed_energy_in_joules = (double)(p->end_energy_data - p->start_energy_data) / p->energy_data_multiplier;
p->consumed_energy_in_kwhs = p->consumed_energy_in_joules / ONE_KWH_IN_JOULES;


//-------------------------------------------------------------------------
// Report consumed energy.
//-------------------------------------------------------------------------
fprintf(stdout, "\nEnergy: [%g] Joule(s) - [%g] kWh(s).\n", p->consumed_energy_in_joules, p->consumed_energy_in_kwhs);

If you read the source of the sample, you will notice that some extra code is dedicated to gracefully end all the child process(s) and remove the PL – if we created an ESRV instance. This is good house cleaning practice. It also allows me to use the “please close the patient” line surgeons usually strikes to the assistants at the end of the operation:).

What if I just want to use the sample?

Sure, you can do that! The source code of the energy utility is attached at the end of this blog entry. You will have to build it for your platform. The following files must be added to the project: productivity_link.c and productivity_link_helper.c; and the following symbols must be defined – in addition of the target OS symbol:

    • __PL_GENERATE_INI__

    • __PL_GENERATE_INI_VERSION_TAGGING__

    • __PL_GENERATE_INI_BUILD_TAGGING__

    • __PL_GENERATE_INI_DATE_AND_TIME_TAGGING__

    • __PL_BLOCKING_COUNTER_FILE_LOCK__



Please refer to section 3.9 of the SDK user guide for details on the build process.

Help!

The listing below was obtained typing energy --help.

energy give the energy usage for a simple command.

Use: energy [options] -- command [arguments...]

[--guid <guid>]:
Read energy data from esrv instance identified by the guid.
[--guid_shell_variable <variable>]:
Read energy data from esrv instance identified by the guid stored in shell variable.
[--channel <integer>]:
Use esrv channel. By default, channel 1 is used.
[--esrv_options <string>]:
Start an esrv instance with options to read energy data from.
By default, the options used are: --device y210 and esrv default settings.
Use esrv --help for details on esrv defaults.
[--version]:
Print version info and exit successfully.
[--help]:
Print this help message and exit successfully.

Notes:
- Energy is given in Joule and kWh -- 1 kWh equals 3,600,000 Joules.
- If neither --guid nor --guid_shell_variable options are used, then energy starts an
instance of esrv. This instance is terminated and its PL is suppressed at
the end of the command execution. Starting an esrv instance may add some
execution time at startup.
- Esrv and support library -- if applicable -- must be in the search path or
in the execution folder. This also includes the library path under non-Windows* OSes.
- User can interrupt the measurement by typing <CTRL>+<C>. The command will
be ended when the next output will be printed.
- Options --guid and --guid_shell_variable are mutually exclusive.

Examples:
energy --dir c:\ /s
energy --guid 4d3e6616-d71c-4b12-8e2a-bee7e3cde0e2 -- dir c:\ /s
energy --guid_shell_variable ESRV_GUID -- dir c:\ /s
-al /usr

What next?

I wanted my second blog entry on the Intel® Energy Checker SDK as the sequel to the first one. Indeed, once you have the means to measure the energy consumed by a system – or to estimate it using a satisfactory model – it is important to show how this data can be used. One use of this data is obviously to measure the energy consumed by our every-day interaction with our computers. I have to confess that while I was developing this SDK, very early in this process, I used to plot the energy consumed by my systems in the background – with a tool similar to the xload utility of X11*. It made me brutally aware that energy is an ever-increasing function over time, and even if you do nothing, the curve just continues to rise, to rise, and to rise even more. Sure we will never be able to stop the growth of the curve without violating the laws of physics. But at least, we can try to minimize the curve’s slope. To do so, we must start writing energy efficient software today!

Jamel Tayeb

energy.c
energy.h

* Third-party trademarks are the property of their respective owners.

-------------------------------
Added on April 22 2010.

Recently I had to perform several energy efficiency tests on a remote system. The energy utility I’ve presented in this blog entry came in very handy. I really wanted a full automation of the ESRV startup process and the setting of the environment variables expected by energy – when used in its simplest form. As a reminder, once all is set, measuring the energy consumed by the system while running a code is as simple as:


energy -- command


So I decided to put together a simple script to solve this once and for all. The script ends any running instance of esrv, starts a fresh instance of ESRV, and updates the .bashrc script of the user. I ended writing a bash script and a gawk program to perform these tasks. And since it makes my life so much easier, I decided to share them with you here. Note that you may want/have to tweak the bash script for your environment.

To run the script - assuming it is in your path - use either way:


. start_esrv


or


start_esrv


Hope that you will find it useful!

Jamel Tayeb

Notes:

    • I decided to copy the script and the gawk program into /bin directory for my convenience. You will likely not do the same.

    • Do not forget to source the .bashrc script if you plan to use energy immediately after running start_esrv from the current instance of the terminal. Next time you will login, .bashrc will be executed automatically and this will set the energy environment variables.



start_esrv

#!/bin/bash
# Note: run as . ./start_script
#-----------------------------------------------------------------------------
# Start displaying information to stdout
#------------------------------------------------------------------------------
clear
echo ESRV CONFIGURATION
echo ==================

#setenforce 0
#------------------------------------------------------------------------------
# Stop esrv.
#------------------------------------------------------------------------------
echo Ending esrv instance...
killall -q esrv

#------------------------------------------------------------------------------
# Set paths
#------------------------------------------------------------------------------
ESRV_BINARY_PATH=/bin
ESRV_LIBRARY_PATH=/lib

#------------------------------------------------------------------------------
# Set esrv options.
# Note:
# The grace time is required if the device to be used by esrv requires
# a long start and configuration time. If the time is not long enough
# then this script may miss the output. Other option is to loop
# until the output file is created.
#------------------------------------------------------------------------------
ESRV_SHELL_VARIABLE_NAME=ESRV_GUID
ESRV_START_GRACE_TIME_IN_SECONDS=5
ENERGY_IN_HUNDREDS_OF_JOULE=0
ENERGY_IN_HUNDREDS_OF_KWH=2
POWER_IN_HUNDREDS_OF_W=6

#------------------------------------------------------------------------------
#ESRV_LIBRARY=yokogawa_wt210.so
#ESRV_LIBRARY=yokogawa_wt230.so
#ESRV_LIBRARY=yokogawa_wt500.so
#ESRV_LIBRARY=yokogawa_wt3000.so
#ESRV_LIBRARY=esrv_simulated_device.so
ESRV_LIBRARY=build_in_yokogawa_wt210

#------------------------------------------------------------------------------
# Create the gawk script to collect the GUID for our PL instance to read.
# gawk script file is saved in gawk.script file.
#------------------------------------------------------------------------------
echo "{ if(\$1==\"Using\") { print(substr(\$3, 2, length(\$3) - 2)); } }" > ./gawk.script

#------------------------------------------------------------------------------
# Start esrv instance
#------------------------------------------------------------------------------
echo Starting esrv instance...
esrv --start --device y210 --interface_options "com=0" --device_options "items=all" --shell > ./esrv.out &
echo "Pausing for $ESRV_START_GRACE_TIME_IN_SECONDS second(s)..."
sleep $ESRV_START_GRACE_TIME_IN_SECONDS

#------------------------------------------------------------------------------
# Collect the GUID for our PL instance created by this instance of esrv.
#------------------------------------------------------------------------------
gawk -f ./gawk.script ./esrv.out > ./guid.out

#------------------------------------------------------------------------------
# Export ESRV_GUID variable
#------------------------------------------------------------------------------
GUID=`cat ./guid.out`
echo "$ESRV_SHELL_VARIABLE_NAME=$GUID" > ./set_guid.script
source ./set_guid.script

#-----------------------------------------------------------------------------
# Cleanup
#------------------------------------------------------------------------------
rm -rf ./gawk.script
rm -rf ./esrv.out
rm -rf ./guid.out
rm -rf ./set_guid.script

#-----------------------------------------------------------------------------
# Display information to stdout
#------------------------------------------------------------------------------
echo ------------------
echo "ESRV_SHELL VARIABLE = "[$ESRV_SHELL_VARIABLE_NAME]
echo "ESRV GUID = "[$GUID]
echo "ESRV LIBRARY = "[$ESRV_LIBRARY]

#-----------------------------------------------------------------------------
# Update UUID in .bashrc
#------------------------------------------------------------------------------
echo Updating .bashrc script...
gawk -f /bin/start_esrv.awk -v esrv_guid=$ESRV_GUID ~/.bashrc > ~/.bashrc.tmp
rm -f ~/.bashrc
cp -f ~/.bashrc.tmp ~/.bashrc
rm -f ~/.bashrc.tmp
echo Done.


start_esrv.awk

BEGIN {

/*************************************************************/
/* Note: */
/* esrv guid is passed using -v option */
/* invoked with ... -v esrv_guid=$GUID ... */
/*************************************************************/
/*************************************************************/
/* Sample of the line to filter-out */
/* export ESRV_GUID = 1785ae67-d50f-4478-93e6-4a93de8e746d */
/* export ENERGY_IN_HUNDREDS_OF_JOULE = 0 */
/* export ENERGY_IN_HUNDREDS_OF_KWH = 2 */
/* export POWER_IN_HUNDREDS_OF_W = 6 */
/*************************************************************/
FS="=";

}

{
if($1 == "export ESRV_GUID") {
next;
}
if($1 == "export ENERGY_IN_HUNDREDS_OF_JOULE") {
next;
}
if($1 == "export ENERGY_IN_HUNDREDS_OF_KWH") {
next;
}
if($1 == "export POWER_IN_HUNDREDS_OF_W") {
next;
} else {
print $0;
}
}

END {

/*********************************************************/
/* Dump esrv variables settings. */
/*********************************************************/
printf("%s%s\n", "export ESRV_GUID=", esrv_guid);
printf("%s\n", "export ENERGY_IN_HUNDREDS_OF_JOULE=0");
printf("%s\n", "export ENERGY_IN_HUNDREDS_OF_KWH=2");
printf("%s\n", "export POWER_IN_HUNDREDS_OF_W=6");
}
有关编译器优化的更完整信息,请参阅优化通知