O'Reilly logo

Web Performance Tuning, 2nd Edition by Patrick Killelea

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Efficiency

Efficiency is usually defined as throughput divided by utilization. When comparing two components, if one has a higher throughput at the same level of utilization, it is regarded as more efficient. If both have the same throughput but one has a lower level of utilization, that one is regarded as more efficient. While useful as a basis for comparing components, this definition is otherwise irrelevant, because it is only a division of two other parameters of performance.

A more useful measure of efficiency is performance per unit cost. This is usually called cost efficiency. Performance tuning is the art of increasing cost efficiency: getting more bang for your buck. In fact, the Internet itself owes its popularity to the fact that it is much more cost-efficient than previously existing alternatives for transferring small amounts of information. Email is vastly more cost-efficient than a letter. Both send about the same amount of information, but email has near-zero latency and near-zero incremental cost; it doesn’t cost you any more to send two emails rather than one.

Web sites providing product information have lower latency and are cheaper than printed brochures. As the throughput of the Internet increases faster than its cost, entire portions of the economy will be replaced with more cost-efficient alternatives, especially in the business-to-business market, which has little sentimentality for old ways. First, relatively static information such as business paperwork, magazines, books, CDs, and videos will be virtualized. Second, the Internet will become a real-time communications medium.

The cost efficiency of the Internet for real-time communications threatens not only the obvious target of telephone carriers, but also the automobile and airline industries. That is, telecommuting threatens physical commuting. Most of the workforce simply moves bits around, either with computers, on the phone, or in face-to-face conversations (which are, in essence, gigabit-per-second, low-latency video connections). It is only these face-to-face conversations that currently require workers to buy cars for the commute to work. Cars are breathtakingly inefficient, and telecommuting represents an opportunity to save money. Look at the number of cars on an urban highway during rush hour. It’s a slow river of metal, fantastically expensive in terms of car purchase, gasoline, driver time, highway construction, insurance, and fatalities. Then consider that most of those cars spend most of the day sitting in a parking lot. Just think of the lost interest on that idle capital. And consider the cost of the parking lot itself, and the office.

As data transmission costs continue to accelerate their fall, car costs cannot fall at the same pace. Gigabit connections between work and home will inevitably be far cheaper than the daily commute, for both the worker and employer. And at gigabit bandwidth, it will feel like you’re really there.

Using a Shell Script

You can easily measure web performance yourself by writing scripts that time the retrieval of HTML from a web server — that is, the latency. If you have the text browser Lynx from the University of Kansas, here’s a simple way to get an idea of the time to get an answer from any server on the Internet:

% time lynx -source http://patrick.net/
0.05user 0.02system 0:00.74elapsed 9%CPU

(On some systems the time command has been replaced with timex). Of course, the startup time for Lynx is included in this, but if you run it twice and throw away the first result, your second result will be fairly accurate and consistent because the Lynx executable won’t have to be loaded from disk. Remember that network and server time is included as well. In fact, this includes all the time that is not system or user time: .67 seconds in the example above, which used a cable modem connection to a fast server. So even in the case of very good network connectivity, the Internet can still take most of the time. You can also get basic latency measurements with the free Perl tool webget in place of Lynx, or the simple GET program installed with the LWP Perl library.

A great thing about Lynx is that it is runnable via a Telnet session, so if you want to see how the performance or your site looks from elsewhere on the Internet, and you have an account somewhere else on the Internet, you can log in to the remote machine and run the time lynx command given earlier. The time given will be the time from the remote point of view. If you’d like to monitor the performance of a web server rather than just measure it once, the following trivial shell script will do just that. Note that we throw out the page we get by redirecting standard output to /dev/null, because we care only about how long it took. The time measurement will come out on standard error.

#!/bin/bash
while true
do
  time lynx -source http://patrick.net/ > /dev/null
  sleep 600
done

If you call the above script mon, then you can capture the results from standard error (file descriptor 2) to a file called log like this in bash:

$ mon 2>log

Using C

Shell scripting is about the least efficient form of programming because a new executable must be forked for almost every line in the script. Here is a tiny C program, modified from the example client in Chapter 11, which prints out the time it takes to retrieve the home page from a web site. It is much more accurate and efficient at run-time, but also much more complex to write. You can download it from http://patrick.net/software/latency.c.

#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>

#define PORT 80
#define BUFSIZE 4000

int main(int argc, char *argv[]) {
 int sockfd, count;
 char *request = "GET / HTTP/1.0\n\n";
 char reply[BUFSIZE];
 struct hostent *he;
 struct sockaddr_in target;
 struct timeval *tvs; /* start time */
 struct timeval *tvf; /* finish time */
 struct timezone *tz;

 if ((he=gethostbyname(argv[1])) == NULL) {
        herror("gethostbyname");
        exit(1);
 }

 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
 }

 target.sin_family = AF_INET;
 target.sin_port = htons(PORT);
 target.sin_addr = *((struct in_addr *)he->h_addr);
 bzero(&(target.sin_zero), 8);

 if (connect(sockfd, (struct sockaddr *)&target,
        sizeof(struct sockaddr)) == -1) {
        perror("connect");
        exit(1);
 }

 tvs = (struct timeval *) malloc(sizeof(struct timeval));
 tvf = (struct timeval *) malloc(sizeof(struct timeval));
 tz = (struct timezone *) malloc(sizeof(struct timezone));

 gettimeofday(tvs, tz);
 send(sockfd, request, strlen(request), 0);
 if ((count = recv(sockfd, reply, BUFSIZE, 0)) == -1) {
        perror("recv");
        exit(1);
 }
 gettimeofday(tvf, tz);

 printf("%d bytes received in %d microseconds\n", count,
        (tvf->tv_sec - tvs->tv_sec) * 1000000 +
        (tvf->tv_usec - tvs->tv_usec));
        close(sockfd);
        return 0;
}

Compile it like this:

% gcc -o latency latency.c

And run it like this:

% latency patrick.net

Here is sample output:

2609 bytes received in 6247 microseconds

Using Perl

While the C program does something we want, it is a bit too low-level to be conveniently maintained or updated. Here is a similar program in Perl, which is more efficient than shell scripting, but less efficient than C. We use the LWP::UserAgent library because it is easier than doing direct socket manipulation. We need to use the Time::HiRes library because timings are to whole second resolution by default in Perl.

#!/usr/local/bin/perl -w
use LWP::UserAgent;
use Time::HiRes 'time','sleep';

$ua = LWP::UserAgent->new;
$request = new HTTP::Request('GET', "http://$ARGV[0]/");
$start = time(  );
$response = $ua->request($request);
$end = time(  );
$latency = $end - $start;

print length($response->as_string(  )), " bytes received in $latency seconds\n";

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required