CS360 Lecture notes -- Time Routines

  • Jian Huang
  • CS360
  • Directory: ~huangj/cs360/notes
  • Lecture notes: http://www.cs.utk.edu/~huangj/cs360/360/notes/time.html

    ANSI C Standard: < time.h >

    We commonly hear complaints as to there is no way to have a portable code that takes time on all operating systems. For instance, using gettimeofday on Unix is fine, but going to Windows, there is no gettimeofday call. There, you are supposed to use a call like clock to measure time.

    In fact, this is merely a confusion caused by historical reasons and a whole community of people that disdain anything related to that M company. Here, let's talk a little bit about the de facto standard way of measuring time, as ANSI C'87 has defined it.

    On any system claiming to support ANSI C, it must have a < time.h >. The ANSI C standard C-library functions are:
         clock_t      clock(void);
         time_t       time(time_t * tp);
         double       difftime(time_t time2, time_t time1);
         time_t       mktime(struct tm * tp);
       
         char *       ctime(const time_t *clock);
         struct tm *  localtime(const time_t *clock);
         struct tm *  gmtime(const time_t *clock);
         char *       asctime(const struct tm *tm);
         size_t       strftime(const char *s, size_t  maxsize,  const  char
                               *format, const struct tm *timeptr);
    

    struct tm is used for parsing time into a human readable format.

    struct tm {
         int   tm_sec;    /* seconds after the minute - [0, 61] */
                          /* for leap seconds */
         int   tm_min;    /* minutes after the hour - [0, 59] */
         int   tm_hour;   /* hour since midnight - [0, 23] */
         int   tm_mday;   /* day of the month - [1, 31] */
         int   tm_mon;    /* months since January - [0, 11] */
         int   tm_year;   /* years since 1900 */
         int   tm_wday;   /* days since Sunday - [0, 6] */
         int   tm_yday;   /* days since January 1 - [0, 365] */
         int   tm_isdst;  /* daylight saving time tag */
    }
    

    In this standard, the return value of time is the calendar time (time_t, which is really just a large arithmetic number) is measured in number of seconds from 00:00:00, January 1, 1970 (the Universally Coordinated Time). clock returns the number of clock ticks since the start of the program, more accurately, the process.

    Calls like localtime and gmtime converts the calendar time into a tm structure in either your local time zone or the Greenwich Mean time (mostly of historical significance).

    To make human understand the current time, of course strings are better than a struct or a time_t. For this purposes, two function calls are designed. ctime and asctime takes a calendar time or a tm struct and converts the time to a string in the form:

        Sun Jan 3 15:14:13 1998\n\0
    

    At last, strftime is a function offering formatting time with all kinds of controls that printf offers.


    Unix gettimeofday

    Coming back to measuring time directly using gettimeofday, it is a system call on Unix. It's synopsis is:

         #include < sys/time.h >
    
         int
         gettimeofday(struct timeval *restrict tp, void *restrict tzp);
    

    The structures pointed to by tp and tzp are defined in sys/time.h as:

         struct timeval {
                 time_t       tv_sec;   /* seconds since Jan. 1, 1970 */
                 suseconds_t  tv_usec;  /* and microseconds */
         };
    

    Wall Clock Time vs. Process Time

    To date, all timing measurements we have talked about falls under the category of calendar time, or wall clock time that is the number of seconds maintained by the system since the Epoch, 00:00:00 January 1, 1970, Coordinated Universal Time (UTC). UTC is also known as Greenwich Mean Time.

    After knowing all about process, let's talk about process time, which is also called CPU time. Process time is a concept that refers to how much central processor resources is used by a process. Just like calendar time, process time is also measured in clock ticks, historically with 50, 60 or 100 ticks per second.

    For each process, the system maintains three values:

      - clock time (wall clock time)
      - user CPU time
      - system CPU time
    

    On Unix, process time is collected by calling

      #include < sys/times.h >
      clock_t times (struct tms * buf);
    
    The tms structure has the following definition:
      struct tms {
        clock_t tms_utime;  /* user CPU time */
        clock_t tms_stime;  /* system CPU time */
        clock_t tms_cutime; /* user CPU time, terminated children */
        clock_t tms_cstime; /* system CPU time, terminated children */
      }
    

    Note, the return value of times is the wall clock time, measured from a arbitrary point in the past. One does not use that absolute value, but rather use the relative. That is, taking the difference between two return values obtained at two points in the program to get the wall clock execution time of that segment of the code. The ANSI C standard time.h can be used to measure wall clock time as well, but not the CPU time.

    The u/stime measures the CPU time that the current process spent in user space or system calls, respectively. cu/stime measures the same quantities for all children processes of the current processes that have been wait'ed for.

    To find out how many ticks there are in each second, use

      #include < unistd.h >
      long clktck = 0;
      clktck = sysconf(_SC_CLK_TCK);
    

    Putting things all together, let's look at the following code.
    /* timetest.c */
    #include < stdio.h >
    #include < stdlib.h >
    
    #include < time.h >
    #include < sys/time.h >
    #include < sys/times.h >
    #include < unistd.h >
    
    typedef struct {
      clock_t        clock_val;
      time_t         time_val;
      struct timeval gettime_val;
      struct tms     times_val; 
    } all_times;
    
    void get_time(all_times * t)
    {
      t->clock_val = clock();
      t->time_val = time(NULL);
      gettimeofday(&(t->gettime_val), NULL);
      times(&(t->times_val));
    }
    
    void print_duration(all_times * t1, all_times * t2)
    {
      float elapsed;
      long clktck, nticks; 
    
      nticks = t2->clock_val - t1->clock_val;
      elapsed = (float)nticks/CLOCKS_PER_SEC; 
      fprintf(stderr,"clock: %f sec\n", elapsed);
    
      nticks = t2->time_val - t1->time_val;
      fprintf(stderr,"time: %d sec\n", nticks);
    
      elapsed = t2->gettime_val.tv_sec - t1->gettime_val.tv_sec;
      elapsed += (t2->gettime_val.tv_usec - t1->gettime_val.tv_usec)/1e6;
      fprintf(stderr,"gettimeofday: %f sec\n", elapsed);
    
      clktck = sysconf(_SC_CLK_TCK);
      nticks = t2->times_val.tms_utime - t1->times_val.tms_utime;
      elapsed = (float)nticks/clktck;  
      fprintf(stderr,"times: %f sec user time\n", elapsed);
    
      nticks = t2->times_val.tms_stime - t1->times_val.tms_stime;
      elapsed = (float)nticks/clktck;  
      fprintf(stderr,"times: %f sec system time\n", elapsed);
    }
    
    long simplecat()
    {
      char c;
      int i;
      long cnt = 0;
    
      i = fread(&c, 1, 1, stdin);
      while(i > 0) {
        fwrite(&c, 1, 1, stdout);
        i = fread(&c, 1, 1, stdin);
        cnt ++;
      }
    
      return cnt;
    }
    
    int main(void)
    {
      int totalbytes;
    
      all_times a1, a2;
    
      get_time(&a1);
    
      totalbytes = simplecat();
    
      get_time(&a2);
      print_duration(&a1, &a2);
    
      fprintf(stderr, "total of %f MiB\n", totalbytes/1e6);
    
      return 0;
    }
    

    The output of the above code looks quite different in two scenarios, let's reason about it a bit.
    UNIX> timetest < gigantic.jpeg > /dev/null
    clock: 2.140000 sec
    time: 2 sec
    gettimeofday: 2.193709 sec
    times: 2.120000 sec user time
    times: 0.020000 sec system time
    total of 59.134079 MiB
    UNIX> timetest 
    1
    1
    2
    2
    3
    3
    4
    4
    5
    5
    6
    6
    clock: 0.000000 sec
    time: 8 sec
    gettimeofday: 7.487184 sec
    times: 0.000000 sec user time
    times: 0.000000 sec system time
    total of 0.000012 MiB