NOTE:

These are a little sketchy...I'll fill in details 'soon'.

Java Timing

There is a wonderful discussion of timing in Java at StackOverflow.

Basic C++ Timing

Recall that we've discussed timing fragments of code as far back as CSC 121. Let's clarify from there and then move on to more accurate ways to time computer events.

Average X...Average Y...Why?

In order to get an 'accurate' timing result with the time() function, we had to make many calls to the function in question and take an average time for them all. This could be complicated if there was setup, in which case we had to time this setup separately and then subtract it from the overall timing.

This sort of averaging helps to get timings which are more accurate than the timing method can produce. time() can only produce times accurate to the whole second, but we can get fractional seconds from timings taken with the averaging method.

There are other factors, however, which complicate timing programs -- especially on a multi-user or multi-processing system. With multiple programs running at once, it can be hard to tell if you are timing just you or some of Joe and some of Sally and some of the OS as well. That is why we try to run such programs 5 or more times -- preferably at different times of day and different operating conditions. Averaging over these runs should help to smooth out the bumps experienced because of machine and OS factors.

Toward More Accuracy

The time() function is not, however, the only timing function available to us. It is just one of the most portable and easily understood (hence its use to time things in the introductory course).

Other candidates (depending on your needs and platform) are: namespace chrono tools, getrusage(), gettimeofday(), and clock(). getrusage() and clock() report time since your program began. gettimeofday() reports the current time of day similiar to time(). Let's look at each in turn.

namespace chrono Tools

The chrono library and namespace come with C++ and offer timings down to the nanosecond depending on hardware support. This process is a little more syntax heavy than that for using the time() function, but well worth it! The basic code can be:

    auto start { std::chrono::steady_clock::now() };
    // do something to be timed
    auto end { std::chrono::steady_clock::now() };
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";

But to get more precision out of it than decimal seconds, just use a more precise clock and change up the duration with a cast:

    double nano, milli;
    auto start { std::chrono::high_resolution_clock::now() };
    // do something to be timed
    auto end { std::chrono::high_resolution_clock::now() };
    milli = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    nano = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
    std::cout << "elapsed time: " << milli << "ms\n";
    std::cout << "elapsed time: " << nano << "ns\n";

Of course, you don't need the std:: if you have a using directive for it.

getrusage()

The getrusage() function is located in sys/resource.h (system files have no C++ translated equivalent header names). It takes two arguments: the first is an integer to tell it whose resource usage statistics you want (possible values are RUSAGE_SELF and RUSAGE_CHILDREN). Its second argument is a pointer to an rusage structure where it should store the collected statistics. getrusage() actually collects more than just time statistics; it also collects statistics about memory usage, page faults, memory swaps, blocked i/o operations, signals, context switches, and IPC messages. It is to timing as a laser is to cutting tomatoes -- a bit of overkill. But, its precision is in microseconds and it is relatively easy to use.

The portions of the struct rusage argument we are interested in are ru_utime and ru_stime -- the user time and system time respectively. Both are structures themselves: struct timeval. This struct has two members: tv_sec and tv_usec. Adding tv_sec to tv_usec*1e-6 will give you the actual number of seconds.

To use getrusage() you can do something like this:

    const unsigned long LOTS = 50000000;
    rusage start, end;

    getrusage(RUSAGE_SELF, &start);

    for (unsigned long i = 0; i != LOTS; i++)
    {
        // do something to be timed
    }

    getrusage(RUSAGE_SELF, &end);

    double diff = end.ru_utime.tv_sec+end.ru_utime.tv_usec*1e-6 -
                  (start.ru_utime.tv_sec+start.ru_utime.tv_usec*1e-6);

    cout << LOTS << " of that took " << diff << " seconds ("
         << (diff/(double)LOTS) << " sec/act) to accomplish." << endl;

Of course, you'll probably want to encapsulate that nasty difference calculation into a nice (inline) function.

gettimeofday()

gettimeofday() is in sys/time.h and reports time since Jan 1, 1970 just as time() does. But its granularity is up to microseconds instead of just seconds.

gettimeofday() also accepts two arguments. Here the first is a pointer to a timeval structure and the second is a pointer to a timezone structure. Use of gettimeofday() is very similar to that for getrusage():

    const unsigned long LOTS = 50000000;
    timeval start, end;

    gettimeofday(&start,nullptr);

    for (unsigned long i = 0; i != LOTS; i++)
    {
        // do something to be timed
    }

    gettimeofday(&end,nullptr);

    double diff = end.tv_sec+end.tv_usec*1e-6 -
                  (start.tv_sec+start.tv_usec*1e-6);

    cout << LOTS << " of that took " << diff << " seconds ("
         << (diff/(double)LOTS) << " sec/act) to accomplish." << endl;

The second argument has been passed as nullptr because we don't really care what the time zone information is to find time differences.

When you time the same thing with getrusage() and gettimeofday() you'll see a significant difference! That's because getrusage() keeps track of processor time spent in your program and processor spent on system code at your program's request separately. gettimeofday(), on the other hand, tracks seconds ellapsed no matter who/what used them up.

The Common Element: struct timeval

Some systems don't fill in timeval structures extremely accurately and might be off a bit. Here's a little code to adjust them (both start and end) before calculating your difference:

    void fix_timeval(struct timeval * tv)
    {
        while (tv->tv_usec < 0)
        {
            tv->tv_sec--;
            tv->tv_usec += 1000000L;
        }
        if (tv->tv_usec >= 1000000L)
        {
            tv->tv_sec += (tv->tv_usec)/1000000L;
            tv->tv_usec %= 1000000L;
        }
        return;
    }

You could probably do the modulo on the negative half, too, but I hate modulos of negatives. *shakes head* They just don't make sense to me. *shrug*

clock()

clock() gives similar precision to getrusage() but takes a different approach. It returns the time in terms of CPU clock cycles. Thus you must divide by CLOCKS_PER_SEC to arrive at a figure measured in seconds. There are several possible caveats with the clock() function, but most won't apply to you here.

Here is some sample code using clock():

    const unsigned long LOTS = 50000000;
    clock_t start, end;

    start = clock();

    for (unsigned long i = 0; i != LOTS; i++)
    {
        // do something to be timed
    }

    end = clock();

    long diff_clock = end-start;
    double diff_sec = diff_clock/(double)CLOCK_PER_SEC;

    cout << LOTS << " of that took " << diff_sec << " seconds ("
         << (diff_sec/LOTS) << " sec/act) to accomplish." << endl;

Other Properties to Consider

First off, there are countless ways to time events in a computer program. They vary by language, by system, and even by what exactly you are trying to time. Always start with the documentation -- be it a reference book, online manual/help pages, Web language guides, etc. If it is a site you simply found (as opposed to an official site like Sun or IEEE), verify by comparing it to 2 to 5 other sites. If they all seem to say the same thing, it's probably pretty close to right. *smile* When worst comes to worst, you can always read the system source files (like /usr/include/sys/time.h) to find out more about the functions you are trying to use.

That said, one good place to find more information is: die.net's Linux man pages. They have nicely formatted and cross-linked man pages -- including those for the C library. They seem to be kept pretty well up to date. And the search is nice, too.

Finally, since the above timing methods are so much more precise than time(), you may not need to perform a loop around multiple executions to get an average. It depends on the speed of the action(s) you are timing and the size of the data set used during the timings. For safety, it is a good idea to do the multiple executions for an average, though.