Introduction
Many of today’s traditional web applications are built with a client server architecture where all the processing is done on the server, and the client is only used to display static content. All interaction with the application must be sent as a request to the server, and then a response is formatted and sent back to the client where is it simply rendered in the browser window. In this interaction, the client is somewhat analogous to a terminal. Very little of its resources are being used to contribute to the experience the user is getting.
Rich Internet Applications (RIAs) represent the next evolution in the traditional web application model. RIAs employ the power of the client to augment the experience by utilizing the processing power that was previously mostly ignored. RIAs are web applications that have the many of the features of traditional desktop applications, as they introduce another layer of processing on the client that handle richer features such as rendering, state management, and storage.
The functionality of a RIA is only limited by the capabilities of the client. Instead of designing for the lowest common denominator, RIA’s have the opportunity to scale their features and interfaces to match what the client can handle. Awareness of the client’s capabilities becomes critical when determining how to scale an application’s features, or how to present the user interface.
The whitepaper details sample code that can be used to report the capabilities and current utilization of the client’s processor using the Linux operating system. This information can be used by RIA designers to effectively scale their applications to the next level.
Detecting Processing Cores
Improving the performance of client based scripts and executables enables designers to enrich the user’s experiences and push the limits of innovation. A proven performance enhancement is to leverage the additional processing bandwidth through the use of multi-threading. Since not every client system will have multiple cores, or the ability to run multiple threads, it becomes necessary for RIA designers to detect the client’s capability and adapt their experience to take full advantage of the client’s processing resources.
The following code demonstrates a straight forward way to detect the number of processors on the clients system:
gboolean getCpuCount(MIDPlatformSvc *obj, guint *cpuCount, GError **error)
{
dbg("Proc Count %ld",sysconf(_SC_NPROCESSORS_CONF) );
//get configurable system variables
*cpuCount = (guint) sysconf(_SC_NPROCESSORS_CONF);
return TRUE;
}
The sysconf() function provides a method for the application to determine the current value of a system configuration. The system variable _SC_NPROCESSORS_CONF is defined in <unistd.h>. Using the information returned, a RIA designer can make the decision to run either single or multi-threaded code on the client’s machine. This can form the bases for a tiered experience tied to the processing power of the client.
Detecting Processor Utilization
Detecting the client’s current CPU utilization also enables RIA designer’s to fine tune the users experience.
The Linux operating system kernel reports various real time metrics in the virtual /proc file system. The /proc/stat file can be used to calculate the current cpu(s) utilization.
A sample /proc/stat file is listed below:
>cat /proc/stat
cpu 506072 235226 266471 1816948896 79812 95 13441
cpu0 194494 127124 83106 905437102 67969 95 11437
cpu1 311577 108101 183365 911511793 11842 0 2003
intr 57889750 148 2 0 0 2 0 0 0 3 1 0 0 4 0 0 0 0 4379636 0 3 0 0 38724164 841 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4981948 9802998 0 0 0 0 0
ctxt 940559982
btime 1219933779
processes 155266
procs_running 1
procs_blocked 0
As you can see, various pieces of information about the kernel activity can be found in this virtual file. The numbers are all represented in USER_HZ or “Jiffies”. Jiffies are typically 1/100 of a second for x86 based systems.
The first “cpu” line represents an aggregate of the cpu(x) lines that follow it. All numbers are represented as the total time since the system booted.
The meanings of the columns are as follows:
User: time spent executing normal user processes
Nice: time spent executing nice processes
System: time spent executing kernel mode process
Idle: time spent in idle
Iowait: time spent waiting for I/O to complete
Irq: time spent servicing hardware interrupts
Softirq: time spent servicing softirqs
The other information in the /proc/stat file can be used to find out other interesting statistics about the computing environment, but since it is not needed to calculate the processors utilization it will not be detailed in this whitepaper.
The CPU utilization metric is a measure of how much of the processor is being used when the call is made. However, as noted above the /proc/stat file reports the total amount of time spent in a certain state since the system booted. In order to determine the utilization correctly, two separate samples need to be taken over a period of time and the difference used to calculate the result. In the code below, there is a 1 second pause between reads:
gboolean get_proc_utilizaton(unsigned long *proc_util)
{
unsigned long long uptime, uptime1, idle, idle1, t_diff;
double cpu_utils;
//establish a baseline
if(!read_proc_stats( &idle, &uptime))
return FALSE;
sleep(1);
//read the stats since baseline
if(!read_proc_stats( &idle1, &uptime1))
return FALSE;
t_diff = uptime1 - uptime;
dbg("Uptime 1 - 2: %lld\n", t_diff);
cpu_utils = (((double)(idle1 - idle))) / t_diff * 100;
cpu_utils = 100 - cpu_utils;
//Round to nearest integer
*proc_util = lrint(cpu_utils);
dbg("CPU Util %lf\n", cpu_utils);
return TRUE;
}
As you can see, the code establishes a baseline via the first call to read_proc_stats(idle, &uptime) by passing pointers to have the function establish the total amout of uptime and idle time. It then goes to sleep for 1 second, and reads the values again. The difference between the two values is used to calculate the total cpu utilzation over that time interval. The reader is encouraged to experiment with the sleep value, keeping in mind that it may possible to reduce the interval to the point where the reads are occuring faster then the kernel updates its statistics.
The actual reading of the /proc/stats function involves summarzing the key values that determine the total time spent actively working vs. idleing. This is done in the
read_proc_stats file, and is shown below:
gboolean read_proc_stats( unsigned long long *idle, unsigned long long *uptime)
{
static char buf[80];
FILE *fp = NULL;
unsigned long long t_user, t_nice, t_system, t_hardirq, t_softirq, t_idle, t_iowait, t_steal;
unsigned long long t_uptime;
if ((fp = fopen("/proc/stat", "r")) == NULL) {
printf("Could not open the /proc/stat");
return FALSE;
}
//Read the file, find the CPU metrics
if(fgets(buf, 80, fp) != NULL) {
if (!strncmp(buf, "cpu ", 4)) {
sscanf(buf + 5, "%llu %llu %llu %llu %llu %llu %llu %llu",&t_user, &t_nice, &t_system, &t_idle, &t_iowait,&t_hardirq, &t_softirq, &t_steal);
t_uptime = t_user + t_nice + t_system + t_idle + t_iowait + t_hardirq + t_softirq + t_steal;
dbg("proc/stat Line: %s\n", buf);
}
}
else{
return FALSE;
}
if(fp)
fclose(fp);
*idle = t_idle;
*uptime = t_uptime;
dbg("Idle Time: %d, Uptime: %lld \n", t_idle, t_uptime);
return TRUE;
}
The /proc/stats file can be read just like any other text file. The code above opens the /proc/stat file for read, then scans the file for the “cpu” line and reads the corresponding columns. Total uptime is calculated as the summation of all the columns, and idle time is already reported by the kernel.
The code above represents a straightforward way to calculate the total processor utilization when the call is made. Readers are encouraged to experiment with the code above to customize it to your needs.
Conclusion
Rich Internet applications represent the next evolution of Web based applications that will bring the end user new experiences by leveraging the computing power of not only the server, but the client. RIA designers can stretch the frontiers of innovation by no longer tailoring the application to the lowest common denominator, but rather tap into the capabilities of the client and utilize the idle processing that was not typically used in yesterday’s client server model. Detecting what the client can and cannot handle is key to making this new model work, and the code above shows how to make smart decisions based on the capabilities of the processor.