C99 and GMP tuning

Vincent Lefevre vincent at vinc17.net
Mon Apr 9 21:30:52 UTC 2018


On 2018-04-09 22:14:50 +0200, Hans Åberg wrote:
> This page [1] says that POSIX clock_gettime [2] with clock id
> CLOCK_PROCESS_CPUTIME_ID offers better resolution than clock.

Under Linux, clock is based on clock_gettime with
CLOCK_PROCESS_CPUTIME_ID, but the result is in microseconds
only because CLOCKS_PER_SEC is 1000000 as required by XSI.

The practical accuracy of clock_gettime with
CLOCK_PROCESS_CPUTIME_ID seems about 0.4 microsecond
(at least on my machine), so that there isn't much difference
between both.

The advantage of getrusage is that you can choose to consider
the user time only.

I'm attaching a program to compare clock related functions
I wrote some time ago.

-- 
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 allows 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 85556 2016-01-05 08:40:05Z vinc17/zira $"

#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

#define TSDIFF_REAL(T1,T2) \
  (T2.tv_sec - T1.tv_sec + ((double) T2.tv_nsec - T1.tv_nsec) / 1e9)

/* TSDIFF_FAST may allow a slightly more accurate measuring of the
   function call. */
#if TSDIFF_FAST
#define TSDIFF(T1,T2) (T2.tv_sec - T1.tv_sec - (T2.tv_nsec < T1.tv_nsec))
#else
#define TSDIFF(T1,T2) TSDIFF_REAL(T1,T2)
#endif

#define TVDIFF_REAL(T1,T2) \
  (T2.tv_sec - T1.tv_sec + ((double) T2.tv_usec - T1.tv_usec) / 1e6)

/* TVDIFF_FAST may allow a slightly more accurate measuring of the
   function call. */
#if TVDIFF_FAST
#define TVDIFF(T1,T2) (T2.tv_sec - T1.tv_sec - (T2.tv_usec < T1.tv_usec))
#else
#define TVDIFF(T1,T2) TVDIFF_REAL(T1,T2)
#endif

#define OUT(T) printf ("Number of calls: %lu (%.12g s)\n", ncalls, (T));

/* Let's use unsigned long instead of clock_t as this type is sufficient
   for our needs, and to make sure that there is no integer overflow in
   case of clock wrapping (as clock_t is a signed type on some machines,
   e.g. under Linux). */
#define LOOP(F,U)                                               \
  do                                                            \
    {                                                           \
      unsigned long c0, c1, c;                                  \
      printf ("Clock units per second: %.8g\n", (double) (U));  \
      ncalls = 1;                                               \
      c0 = c1 = (F);                                            \
      do                                                        \
        {                                                       \
          ncalls++;                                             \
          c = (F);                                              \
          if (d >= 0 && c - c1 >= (U) * d)                      \
            {                                                   \
              OUT ((double) (c - c0) / (U));                    \
              c1 = c;                                           \
            }                                                   \
        }                                                       \
      while (c - c0 < (U) * NSEC);                              \
      t = (double) (c - c0) / (U);                              \
    }                                                           \
  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
  };

int err (const char *s)
{
  int errnum = errno;
  fprintf (stderr, "clock-times: %s failed (%s)\n", s, strerror (errnum));
  return 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)
    {
      LOOP (clock (), CLOCKS_PER_SEC);
    }
  else if (strcmp (argv[1], "getrusage") == 0)  /* user time only */
    {
      struct rusage usage;
      struct timeval c0, c1;
      ncalls = 1;
      if (getrusage (RUSAGE_SELF, &usage))
        return err ("getrusage");
      c0 = c1 = usage.ru_utime;
      do
        {
          ncalls++;
          if (getrusage (RUSAGE_SELF, &usage))
            return err ("getrusage");
          if (d >= 0 && TVDIFF (c1, usage.ru_utime) >= d)
            {
              OUT (TVDIFF_REAL (c0, usage.ru_utime));
              c1 = usage.ru_utime;
            }
        }
      while (TVDIFF (c0, usage.ru_utime) < NSEC);
      t = TVDIFF_REAL (c0, usage.ru_utime);
    }
  else if (strcmp (argv[1], "times") == 0)
    {
      struct tms buffer;
      clock_t clock_ticks;

      /* Note: here we retrieve the wall-clock time! */
      clock_ticks = sysconf (_SC_CLK_TCK);
      LOOP (times (&buffer), clock_ticks);
    }
  else if (strncmp (argv[1], "CLOCK_", 6) == 0)
    {
      struct timespec c0, c1, c;
      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:
      if (clock_getres (clk_id, &c))
        return 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);
      ncalls = 1;
      if (clock_gettime (clk_id, &c1))
        return err ("clock_gettime");
      c0 = c1;
      do
        {
          ncalls++;
          if (clock_gettime (clk_id, &c))
            return err ("clock_gettime");
          if (d >= 0 && TSDIFF (c1, c) >= d)
            {
              OUT (TSDIFF_REAL (c0, c));
              c1 = c;
            }
        }
      while (TSDIFF (c0, c) < NSEC);
      t = TSDIFF_REAL (c0, c);
    }
  else
    goto usage;

  OUT (t);
  return 0;
}


More information about the gmp-discuss mailing list