C99 and GMP tuning

Vincent Lefevre vincent at vinc17.net
Tue Apr 10 12:27:47 UTC 2018


First, I've attached a new version of my clock-times.c program
(note: it was initially written to measure the performance of
the time functions, but it can also be used to get resolution
and accuracy information by providing a second argument, like
1.000000001).

On 2018-04-09 17:45:16 +0200, Torbjorn Granlund wrote:
> sav_ix at ukr.net writes:
> 
>   Presumably this should lead to dropping support of old systems, which
>   compilers do not provide C99 support. On the other side, more recent
>   OSes provide better 'clock()' implementation, which accuracy
>   (depending of used OS) in times or magnitudes better, compared to
>   'getrusage()'.
> 
>   After moving to C99, can GMP Developers consider to drop use of non
>   portable 'getrusage()', 'rusage' etc. in favor of 'clock()'? The
>   advantage in addition to portability could be that the running time of
>   the tuning be shortened (need be verified).
> 
> This seems completely unrelated to C90 vs C99.
> 
> I also think you're wrong.  If you are aware of a specific system where
> clock() is more accurate than getrusage, then let us know.  But please
> don't base that on beliefs, instead check that it is actually the case.

Here's an example, on a Debian GNU/Linux 8 (jessie) machine:

patate:.../testcases/c-impl> ./clock-times clock 1.000000001
Clock units per second: 1000000
Number of calls: 3674488 (1.000001 s)
Number of calls: 7326182 (2.000001 s)
Number of calls: 10974092 (3 s)
patate:.../testcases/c-impl> ./clock-times getrusage 1.000000001
Number of calls: 20205917 (1.004 s)
Number of calls: 39477071 (2.004 s)
Number of calls: 57081362 (3 s)

This is still the case even on a Debian GNU/Linux 9 (stretch)
machine, i.e. with the latest Debian/stable:

joooj:.../testcases/c-impl> ./clock-times clock 1.000000001
Clock units per second: 1000000
Number of calls: 297212 (1.000003 s)
Number of calls: 588038 (2.000003 s)
Number of calls: 886368 (3.000002 s)
joooj:.../testcases/c-impl> ./clock-times getrusage 1.000000001
Number of calls: 709220 (1.004 s)
Number of calls: 1346154 (2.004 s)
Number of calls: 2043699 (3 s)

With Debian/unstable, this is equivalent:

cventin:.../testcases/c-impl> ./clock-times clock 1.000000001
Clock units per second: 1000000
Number of calls: 1706929 (1.000001 s)
Number of calls: 3423555 (2.000001 s)
Number of calls: 5140163 (3 s)
cventin:.../testcases/c-impl> ./clock-times getrusage 1.000000001
Number of calls: 6191249 (1.000001 s)
Number of calls: 12118336 (2.000001 s)
Number of calls: 18049410 (3 s)

So, on these examples, clock may win compared to getrusage,
but it does not lose.

-- 
Vincent Lefèvre <vincent at vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
-------------- next part --------------
/* Performance of clock(), times() and clock_gettime() function calls.
 *
 * Usage: clock-times <type> [ <num> ]
 * where <type> is the type of the clock (see the code below) and
 * <num> is the interval between two outputs; if set to something
 * like 1.000000001, this also allows one to have an idea on the
 * accuracy. By default, the test is done on 3 seconds (either the
 * CPU time or the wall-clock time, depending on the type); this
 * can be changed by compiling with -DNSEC=... (but in practice,
 * dealing with <num> should be sufficient).
 *
 * When comparing the results, be aware of the difference between
 * the CPU time and the wall-clock time if the process doesn't take
 * 100% CPU time!
 *
 * Man pages under Linux:
 *   time(7) - overview of time and timers
 *   clock(3), clock(3posix) - report CPU time used
 *   times(2), times(3posix) - get process times
 *   clock_getres(2), clock_getres(3posix) - clock and timer functions
 *     (clock_getres, clock_gettime, clock_settime)
 *   getrusage(2), getrusage(3posix) - get resource usage
 *   gettimeofday(2), gettimeofday(3posix) - get the date and time
 *
 * See also: http://stackoverflow.com/q/12392278/3782797 (Measure time
 * in Linux - getrusage vs clock_gettime vs clock vs gettimeofday?) and
 * https://www.gnu.org/software/libc/manual/html_node/Date-and-Time.html
 * specifically for GNU systems.
 *
 * Note: Using clock() may not be a good idea, except for portability,
 * since the result includes the system time (on GNU systems), which
 * generally depends on external events. However, in most cases, the
 * system time should remain much smaller than the user time.
 * About user time vs system time:
 *   http://stackoverflow.com/q/556405/3782797
 *
 * TODO: add times-u (user time) and times-c (cumulated user+sys times).
 */

#define SVNID "$Id: clock-times.c 107487 2018-04-10 11:37:32Z vinc17/cventin $"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>         /* for sysconf() */
#include <time.h>           /* for clock() - processor time */
#include <sys/times.h>      /* for times() - real time */
#include <sys/resource.h>   /* for getrusage() */

#ifndef NSEC
#define NSEC 3
#endif

static inline double tsdiff (struct timespec t1, struct timespec t0)
{
  return (t1.tv_sec - t0.tv_sec + ((double) t1.tv_nsec - t0.tv_nsec) / 1e9);
}

static inline double tvdiff (struct timeval t1, struct timeval t0)
{
  return (t1.tv_sec - t0.tv_sec + ((double) t1.tv_usec - t0.tv_usec) / 1e6);
}

static inline void out (unsigned long ncalls, double t)
{
  static unsigned long old_ncalls = 0;

  if (ncalls != old_ncalls)
    {
      printf ("Number of calls: %lu (%.12g s)\n", ncalls, (t));
      old_ncalls = ncalls;
    }
}

#define LOOP(T,DIFF)                                            \
  do                                                            \
    {                                                           \
      T c0, c;                                                  \
      int n = 1;                                                \
                                                                \
      ncalls = 1;                                               \
      F(c0);                                                    \
      do                                                        \
        {                                                       \
          ncalls++;                                             \
          F(c);                                                 \
          t = (DIFF);                                           \
          if (d >= 0 && t >= n * d)                             \
            {                                                   \
              out (ncalls, t);                                  \
              n++;                                              \
            }                                                   \
        }                                                       \
      while (t < NSEC);                                         \
    }                                                           \
  while (0)

/* See /usr/include/linux/time.h */
struct { clockid_t id; char *s; } clocks[] =
  {
    { CLOCK_REALTIME, "REALTIME" },
#ifdef CLOCK_MONOTONIC
    { CLOCK_MONOTONIC, "MONOTONIC" },
#endif
#ifdef CLOCK_REALTIME_COARSE
    { CLOCK_REALTIME_COARSE, "REALTIME_COARSE" },
#endif
#ifdef CLOCK_MONOTONIC_COARSE
    { CLOCK_MONOTONIC_COARSE, "MONOTONIC_COARSE" },
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
    { CLOCK_PROCESS_CPUTIME_ID, "PROCESS_CPUTIME_ID" },
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
    { CLOCK_THREAD_CPUTIME_ID, "THREAD_CPUTIME_ID" },
#endif
  };

static void err (const char *s)
{
  int errnum = errno;
  fprintf (stderr, "clock-times: %s failed (%s)\n", s, strerror (errnum));
  exit (2);
}

int main (int argc, char **argv)
{
  unsigned long ncalls;
  double d = -1, t;

  if (argc != 2 && argc != 3)
    {
    usage:
      fprintf (stderr, "Usage: clock-times "
               "\"clock\" | \"times\" | \"CLOCK_\"... [ num ]\n");
      return 1;
    }

  if (argc == 3)
    {
      char *end;

      d = strtod (argv[2], &end);
    }

  if (strcmp (argv[1], "clock") == 0)
    {
#undef F
#define F(C) C = clock ()

      printf ("Clock units per second: %.8g\n", (double) CLOCKS_PER_SEC);
      LOOP (double, (c - c0) / CLOCKS_PER_SEC);
    }
  else if (strcmp (argv[1], "getrusage") == 0)  /* user time only */
    {
      struct rusage usage;

#undef F
#define F(C)                                    \
      do                                        \
        {                                       \
          if (getrusage (RUSAGE_SELF, &usage))  \
            err ("getrusage");                  \
          C = usage.ru_utime;                   \
        }                                       \
      while (0);

      LOOP (struct timeval, tvdiff (c, c0));
    }
  else if (strcmp (argv[1], "times") == 0)
    {
      struct tms buffer;
      clock_t clock_ticks;

#undef F
#define F(C) C = times (&buffer)

      /* Note: here we retrieve the wall-clock time! */
      clock_ticks = sysconf (_SC_CLK_TCK);
      printf ("Clock units per second: %.8g\n", (double) clock_ticks);
      LOOP (double, (c - c0) / clock_ticks);
    }
  else if (strncmp (argv[1], "CLOCK_", 6) == 0)
    {
      clockid_t clk_id;
      char *ptr = argv[1] + 6, *endptr, *s = NULL;

      if (*ptr != '\0' &&
          (clk_id = strtol (argv[1] + 6, &endptr, 10), *endptr == '\0'))
        {
          int i;
          for (i = 0; i < sizeof clocks / sizeof clocks[0]; i++)
            if (clocks[i].id == clk_id)
              {
                s = clocks[i].s;
                goto id_ok;
              }
        }
      else
        {
          int i;
          for (i = 0; i < sizeof clocks / sizeof clocks[0]; i++)
            if (strcmp (clocks[i].s, ptr) == 0)
              {
                clk_id = clocks[i].id;
                s = clocks[i].s;
                goto id_ok;
              }
          fprintf (stderr, "clock-times: unknown clock_gettime clock type\n");
          return 1;
        }

    id_ok:
      {
        struct timespec c;

        if (clock_getres (clk_id, &c))
          err ("clock_getres");
        printf ("Clock resolution (id %ld [CLOCK_%s]): ",
                (long) clk_id, s ? s : "?");
        if (c.tv_sec)
          printf ("%ld.%09ld s\n", (long) c.tv_sec, c.tv_nsec);
        else
          printf ("%ld ns\n", c.tv_nsec);
      }

#undef F
#define F(C) clock_gettime (clk_id, &C) && (err ("clock_gettime"), 0)

      LOOP (struct timespec, tsdiff (c, c0));
    }
  else
    goto usage;

  out (ncalls, t);
  return 0;
}


More information about the gmp-discuss mailing list