Patchwork [libfortran] Use high resolution clock if available, timing cleanup

login
register
mail settings
Submitter Janne Blomqvist
Date Jan. 31, 2011, 8:18 p.m.
Message ID <AANLkTikNqhqPaeAYdf4nac_NrGEGBFvYpEpcJRx=o3UK@mail.gmail.com>
Download mbox | patch
Permalink /patch/81226/
State New
Headers show

Comments

Janne Blomqvist - Jan. 31, 2011, 8:18 p.m.
Hi,

the attached patch

1) Does some cleanup for CPU timing functions.

2) Uses clock_gettime() if available, and if so in particular:
      2a) enables up to nanosecond resolution for system_clock_8.
      2b) SYSTEM_CLOCK uses the monotonic clock, if available, which
is a better choice for a wallclock timing function.

Regtested on x86_64-unknown-linux-gnu, Ok for trunk?

2011-01-31  Janne Blomqvist  <jb@gcc.gnu.org>

	* configure.ac: Check for clock_gettime().
	* configure: Regenerated.
	* config.h.in: Regenerated.
	* intrinsics/time_1.h (__time_1): Rename to gf_cputime, add
	times() fallback.
	(gf_gettime): New function.
	* intrinsics/cpu_time.c (__cpu_time_1): Update to call gf_cputime.
	* intrinsics/date_and_time.c (date_and_time): Use gf_gettime.
	* intrinsics/dtime.c (dtime_sub): Use gf_cputime.
	* intrinsics/etime.c (etime_sub): Use gf_cputime.
	* intrinsics/system_clock.c (system_clock_4): Use gf_gettime.
	(system_clock_8): Use gf_gettime, increase count rate to allow
	nanosecond precision, remove overflow prone branch.
Steve Kargl - Jan. 31, 2011, 8:46 p.m.
On Mon, Jan 31, 2011 at 10:18:45PM +0200, Janne Blomqvist wrote:
> 2011-01-31  Janne Blomqvist  <jb@gcc.gnu.org>
> 
> 	* configure.ac: Check for clock_gettime().
> 	* configure: Regenerated.
> 	* config.h.in: Regenerated.
> 	* intrinsics/time_1.h (__time_1): Rename to gf_cputime, add
> 	times() fallback.
> 	(gf_gettime): New function.
> 	* intrinsics/cpu_time.c (__cpu_time_1): Update to call gf_cputime.
> 	* intrinsics/date_and_time.c (date_and_time): Use gf_gettime.
> 	* intrinsics/dtime.c (dtime_sub): Use gf_cputime.
> 	* intrinsics/etime.c (etime_sub): Use gf_cputime.
> 	* intrinsics/system_clock.c (system_clock_4): Use gf_gettime.
> 	(system_clock_8): Use gf_gettime, increase count rate to allow
> 	nanosecond precision, remove overflow prone branch.
> 

OK.

One comment.  Why

+AC_DEFINE([HAVE_FEENABLEEXCEPT],[1],[libm includes feenableexcept])])

I did not see a use of fenv facilities in a quick scan of
the patch.
Janne Blomqvist - Jan. 31, 2011, 8:53 p.m.
On Mon, Jan 31, 2011 at 22:46, Steve Kargl
<sgk@troutmask.apl.washington.edu> wrote:
> On Mon, Jan 31, 2011 at 10:18:45PM +0200, Janne Blomqvist wrote:
>> 2011-01-31  Janne Blomqvist  <jb@gcc.gnu.org>
>>
>>       * configure.ac: Check for clock_gettime().
>>       * configure: Regenerated.
>>       * config.h.in: Regenerated.
>>       * intrinsics/time_1.h (__time_1): Rename to gf_cputime, add
>>       times() fallback.
>>       (gf_gettime): New function.
>>       * intrinsics/cpu_time.c (__cpu_time_1): Update to call gf_cputime.
>>       * intrinsics/date_and_time.c (date_and_time): Use gf_gettime.
>>       * intrinsics/dtime.c (dtime_sub): Use gf_cputime.
>>       * intrinsics/etime.c (etime_sub): Use gf_cputime.
>>       * intrinsics/system_clock.c (system_clock_4): Use gf_gettime.
>>       (system_clock_8): Use gf_gettime, increase count rate to allow
>>       nanosecond precision, remove overflow prone branch.
>>
>
> OK.

Thanks for the quick review!

Sending        libgfortran/ChangeLog
Sending        libgfortran/config.h.in
Sending        libgfortran/configure
Sending        libgfortran/configure.ac
Sending        libgfortran/intrinsics/cpu_time.c
Sending        libgfortran/intrinsics/date_and_time.c
Sending        libgfortran/intrinsics/dtime.c
Sending        libgfortran/intrinsics/etime.c
Sending        libgfortran/intrinsics/system_clock.c
Sending        libgfortran/intrinsics/time_1.h
Transmitting file data ..........
Committed revision 169449.


> One comment.  Why
>
> +AC_DEFINE([HAVE_FEENABLEEXCEPT],[1],[libm includes feenableexcept])])
>
> I did not see a use of fenv facilities in a quick scan of
> the patch.

That was just stuff surrounding the part my patch changes in configure.ac.
Tobias Burnus - Jan. 31, 2011, 8:57 p.m.
Janne Blomqvist wrote:
> 2) Uses clock_gettime() if available, and if so in particular:
>        2a) enables up to nanosecond resolution for system_clock_8.

I wonder whether one should update 
http://gcc.gnu.org/onlinedocs/gfortran/CPU_005fTIME.html - I maybe it 
would be useful to document that a REAL(8) argument leads to higher 
precision than REAL(4).

Tobias
Janne Blomqvist - Jan. 31, 2011, 10:08 p.m.
On Mon, Jan 31, 2011 at 22:57, Tobias Burnus <burnus@net-b.de> wrote:
> Janne Blomqvist wrote:
>>
>> 2) Uses clock_gettime() if available, and if so in particular:
>>       2a) enables up to nanosecond resolution for system_clock_8.
>
> I wonder whether one should update
> http://gcc.gnu.org/onlinedocs/gfortran/CPU_005fTIME.html - I maybe it would
> be useful to document that a REAL(8) argument leads to higher precision than
> REAL(4).

Yes, that would be a good idea. In particular, SYSTEM_CLOCK with
kind=8 arguments gives nanosecond resolution vs. millisecond with
kind=4 arguments (otherwise the count would wrap around too fast).
Also, like many other intrinsics, SYSTEM_CLOCK doesn't conform to the
F2003 requirement as it doesn't allow arbitrary combinations of
integer kinds for the arguments.

FWIW, the documentation part is

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42434
Hans-Peter Nilsson - Feb. 1, 2011, 2:38 a.m.
On Mon, 31 Jan 2011, Janne Blomqvist wrote:
> the attached patch
>
> 1) Does some cleanup for CPU timing functions.
>
> 2) Uses clock_gettime() if available, and if so in particular:
>       2a) enables up to nanosecond resolution for system_clock_8.
>       2b) SYSTEM_CLOCK uses the monotonic clock, if available, which
> is a better choice for a wallclock timing function.

3) Uses errno without including errno.h (or the equivalent
action), breaking build on some targets.

For cris-elf (or presumably any newlib-based target) cutnpasted:

libtool: compile:
/tmp/hpautotest-gcc1/cris-elf/gccobj/./gcc/xgcc
-B/tmp/hpautotest-gcc1/cris-elf/gccobj/./gcc/ -nostdinc
-B/tmp/hpautotest-gcc1/cris-elf/gccobj/cris-elf/newlib/ -isystem
/tmp/hpautotest-gcc1/cris-elf/gccobj/cris-elf/newlib/targ-include
-isystem /tmp/hpautotest-gcc1/gcc/newlib/libc/include
-B/tmp/hpautotest-gcc1/cris-elf/gccobj/cris-elf/libgloss/cris
-L/tmp/hpautotest-gcc1/cris-elf/gccobj/cris-elf/libgloss/libnosys
-L/tmp/hpautotest-gcc1/gcc/libgloss/cris
-B/tmp/hpautotest-gcc1/cris-elf/pre/cris-elf/bin/
-B/tmp/hpautotest-gcc1/cris-elf/pre/cris-elf/lib/ -isystem
/tmp/hpautotest-gcc1/cris-elf/pre/cris-elf/include -isystem
/tmp/hpautotest-gcc1/cris-elf/pre/cris-elf/sys-include
-DHAVE_CONFIG_H -I. -I/tmp/hpautotest-gcc1/gcc/libgfortran
-iquote/tmp/hpautotest-gcc1/gcc/libgfortran/io
-I/tmp/hpautotest-gcc1/gcc/libgfortran/../gcc
-I/tmp/hpautotest-gcc1/gcc/libgfortran/../gcc/config
-I../.././gcc -D_GNU_SOURCE -std=gnu99 -Wall -Wstrict-prototypes
-Wmissing-prototypes -Wold-style-definition -Wextra
-Wwrite-strings -fcx-fortran-rules -ffunction-sections
-fdata-sections -g -O2 -MT cpu_time.lo -MD -MP -MF
.deps/cpu_time.Tpo -c
/tmp/hpautotest-gcc1/gcc/libgfortran/intrinsics/cpu_time.c -o
cpu_time.o
In file included from
/tmp/hpautotest-gcc1/gcc/libgfortran/intrinsics/cpu_time.c:26:0:
/tmp/hpautotest-gcc1/gcc/libgfortran/intrinsics/time_1.h: In
function 'gf_cputime':
/tmp/hpautotest-gcc1/gcc/libgfortran/intrinsics/time_1.h:158:32:
error: 'errno' undeclared (first use in this function)
/tmp/hpautotest-gcc1/gcc/libgfortran/intrinsics/time_1.h:158:32:
note: each undeclared identifier is reported only once for each
function it appears in
make[3]: *** [cpu_time.lo] Error 1
make[3]: Leaving directory
`/tmp/hpautotest-gcc1/cris-elf/gccobj/cris-elf/libgfortran'
make[2]: *** [all] Error 2

brgds, H-P
Janne Blomqvist - Feb. 1, 2011, 8:28 a.m.
On Tue, Feb 1, 2011 at 04:38, Hans-Peter Nilsson <hp@bitrange.com> wrote:
> On Mon, 31 Jan 2011, Janne Blomqvist wrote:
>> the attached patch
>>
>> 1) Does some cleanup for CPU timing functions.
>>
>> 2) Uses clock_gettime() if available, and if so in particular:
>>       2a) enables up to nanosecond resolution for system_clock_8.
>>       2b) SYSTEM_CLOCK uses the monotonic clock, if available, which
>> is a better choice for a wallclock timing function.
>
> 3) Uses errno without including errno.h (or the equivalent
> action), breaking build on some targets.

Thanks for the report. Committed r169467 as obvious.

Patch

diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac
index e8f842c..ed1e2cc 100644
--- a/libgfortran/configure.ac
+++ b/libgfortran/configure.ac
@@ -252,6 +252,7 @@  AC_CHECK_FUNCS(sleep time ttyname signal alarm ctime clock access fork execl)
 AC_CHECK_FUNCS(wait setmode execvp pipe dup2 close fdopen strcasestr getrlimit)
 AC_CHECK_FUNCS(gettimeofday stat fstat lstat getpwuid vsnprintf dup getcwd)
 AC_CHECK_FUNCS(localtime_r gmtime_r strerror_r getpwuid_r ttyname_r ctime_r)
+AC_CHECK_FUNCS(clock_gettime)
 
 # Check for glibc backtrace functions
 AC_CHECK_FUNCS(backtrace backtrace_symbols)
@@ -483,6 +484,16 @@  LIBGFOR_CHECK_FLOAT128
 # Check for GNU libc feenableexcept
 AC_CHECK_LIB([m],[feenableexcept],[have_feenableexcept=yes AC_DEFINE([HAVE_FEENABLEEXCEPT],[1],[libm includes feenableexcept])])
 
+# At least for glibc, clock_gettime is in librt.  But don't pull that
+# in if it still doesn't give us the function we want.
+# This test is copied from libgomp.
+if test $ac_cv_func_clock_gettime = no; then
+  AC_CHECK_LIB(rt, clock_gettime,
+    [LIBS="-lrt $LIBS"  
+     AC_DEFINE(HAVE_CLOCK_GETTIME, 1,
+               [Define to 1 if you have the `clock_gettime' function.])])
+fi
+
 # Check for SysV fpsetmask
 LIBGFOR_CHECK_FPSETMASK
 
diff --git a/libgfortran/intrinsics/cpu_time.c b/libgfortran/intrinsics/cpu_time.c
index 8bffe65..619f8d2 100644
--- a/libgfortran/intrinsics/cpu_time.c
+++ b/libgfortran/intrinsics/cpu_time.c
@@ -1,7 +1,7 @@ 
 /* Implementation of the CPU_TIME intrinsic.
-   Copyright (C) 2003, 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -25,49 +25,23 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "libgfortran.h"
 #include "time_1.h"
 
-/* The most accurate way to get the CPU time is getrusage ().
-   If we have times(), that's good enough, too.  */
-#if !defined (HAVE_GETRUSAGE) || !defined (HAVE_SYS_RESOURCE_H)
-/* For times(), we _must_ know the number of clock ticks per second.  */
-#  if defined (HAVE_TIMES) && (defined (HZ) || defined (_SC_CLK_TCK) || defined (CLK_TCK))
-#    ifdef HAVE_SYS_PARAM_H
-#      include <sys/param.h>
-#    endif
-#    if defined (HAVE_SYS_TIMES_H)
-#      include <sys/times.h>
-#    endif
-#    ifndef HZ
-#      if defined _SC_CLK_TCK
-#        define HZ  sysconf(_SC_CLK_TCK)
-#      else
-#        define HZ  CLK_TCK
-#      endif
-#    endif
-#  endif  /* HAVE_TIMES etc.  */
-#endif  /* !HAVE_GETRUSAGE || !HAVE_SYS_RESOURCE_H  */
 
 static inline void __cpu_time_1 (long *, long *) ATTRIBUTE_ALWAYS_INLINE;
 
 static inline void
 __cpu_time_1 (long *sec, long *usec)
 {
-#if defined(__MINGW32__) || defined (HAVE_GETRUSAGE) && defined (HAVE_SYS_RESOURCE_H)
   long user_sec, user_usec, system_sec, system_usec;
-  __time_1 (&user_sec, &user_usec, &system_sec, &system_usec);
-  *sec = user_sec + system_sec;
-  *usec = user_usec + system_usec;
-#else /* ! HAVE_GETRUSAGE || ! HAVE_SYS_RESOURCE_H  */
-#ifdef HAVE_TIMES
-  struct tms buf;
-  times (&buf);
-  *sec = 0;
-  *usec = (buf.tms_utime + buf.tms_stime) * (1000000 / HZ);
-#else /* ! HAVE_TIMES */
-  /* We have nothing to go on.  Return -1.  */
-  *sec = -1;
-  *usec = 0;
-#endif  /* HAVE_TIMES */
-#endif  /* __MINGW32__ || HAVE_GETRUSAGE */
+  if (gf_cputime (&user_sec, &user_usec, &system_sec, &system_usec) == 0)
+    {
+      *sec = user_sec + system_sec;
+      *usec = user_usec + system_usec;
+    }
+  else
+    {
+      *sec = -1;
+      *usec = 0;
+    }
 }
 
 
diff --git a/libgfortran/intrinsics/date_and_time.c b/libgfortran/intrinsics/date_and_time.c
index dea835b..714df14 100644
--- a/libgfortran/intrinsics/date_and_time.c
+++ b/libgfortran/intrinsics/date_and_time.c
@@ -1,5 +1,5 @@ 
 /* Implementation of the DATE_AND_TIME intrinsic.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011
    Free Software Foundation, Inc.
    Contributed by Steven Bosscher.
 
@@ -29,21 +29,7 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include <assert.h>
 #include <stdlib.h>
 
-#undef HAVE_NO_DATE_TIME
-#if TIME_WITH_SYS_TIME
-#  include <sys/time.h>
-#  include <time.h>
-#else
-#  if HAVE_SYS_TIME_H
-#    include <sys/time.h>
-#  else
-#    ifdef HAVE_TIME_H
-#      include <time.h>
-#    else
-#      define HAVE_NO_DATE_TIME
-#    endif  /* HAVE_TIME_H  */
-#  endif  /* HAVE_SYS_TIME_H  */
-#endif  /* TIME_WITH_SYS_TIME  */
+#include "time_1.h"
 
 #ifndef abs
 #define abs(x) ((x)>=0 ? (x) : -(x))
@@ -176,28 +162,12 @@  date_and_time (char *__date, char *__time, char *__zone,
   struct tm local_time;
   struct tm UTC_time;
 
-#if HAVE_GETTIMEOFDAY
-  {
-    struct timeval tp;
-
-    if (!gettimeofday (&tp, NULL))
-      {
-         lt = tp.tv_sec;
-         values[7] = tp.tv_usec / 1000;
-      }
-    else
-      {
-         lt = time (NULL);
-         values[7] = 0;
-      }
-  }
-#else
-  lt = time (NULL);
-  values[7] = 0;
-#endif /* HAVE_GETTIMEOFDAY */
+  long nanosecs;
 
-  if (lt != (time_t) -1)
+  if (!gf_gettime(GF_CLOCK_REALTIME, &lt, &nanosecs))
     {
+      values[7] = nanosecs / 1000000;
+
       localtime_r (&lt, &local_time);
       gmtime_r (&lt, &UTC_time);
 
diff --git a/libgfortran/intrinsics/dtime.c b/libgfortran/intrinsics/dtime.c
index d1eb912..e36e1f1 100644
--- a/libgfortran/intrinsics/dtime.c
+++ b/libgfortran/intrinsics/dtime.c
@@ -1,7 +1,8 @@ 
 /* Implementation of the dtime intrinsic.
-   Copyright (C) 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011 Free Software
+   Foundation, Inc.
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -47,7 +48,7 @@  dtime_sub (gfc_array_r4 *t, GFC_REAL_4 *result)
     runtime_error ("Insufficient number of elements in TARRAY.");
 
   __gthread_mutex_lock (&dtime_update_lock);
-  if (__time_1 (&user_sec, &user_usec, &system_sec, &system_usec) == 0)
+  if (gf_cputime (&user_sec, &user_usec, &system_sec, &system_usec) == 0)
     {
       tu = (GFC_REAL_4) ((user_sec - us) + 1.e-6 * (user_usec - uu));
       ts = (GFC_REAL_4) ((system_sec - ss) + 1.e-6 * (system_usec - su));
diff --git a/libgfortran/intrinsics/etime.c b/libgfortran/intrinsics/etime.c
index b0fd742..d90bc30 100644
--- a/libgfortran/intrinsics/etime.c
+++ b/libgfortran/intrinsics/etime.c
@@ -1,8 +1,9 @@ 
 /* Implementation of the ETIME intrinsic.
-   Copyright (C) 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011 Free Software
+   Foundation, Inc.
    Contributed by Steven G. Kargl <kargls@comcast.net>.
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -38,7 +39,7 @@  etime_sub (gfc_array_r4 *t, GFC_REAL_4 *result)
   if (((GFC_DESCRIPTOR_EXTENT(t,0))) < 2)
     runtime_error ("Insufficient number of elements in TARRAY.");
 
-  if (__time_1 (&user_sec, &user_usec, &system_sec, &system_usec) == 0)
+  if (gf_cputime (&user_sec, &user_usec, &system_sec, &system_usec) == 0)
     {
       tu = (GFC_REAL_4)(user_sec + 1.e-6 * user_usec);
       ts = (GFC_REAL_4)(system_sec + 1.e-6 * system_usec);
diff --git a/libgfortran/intrinsics/system_clock.c b/libgfortran/intrinsics/system_clock.c
index b07d434..3715562 100644
--- a/libgfortran/intrinsics/system_clock.c
+++ b/libgfortran/intrinsics/system_clock.c
@@ -1,5 +1,6 @@ 
 /* Implementation of the SYSTEM_CLOCK intrinsic.
-   Copyright (C) 2004, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2007, 2009, 2010, 2011 Free Software
+   Foundation, Inc.
 
 This file is part of the GNU Fortran runtime library (libgfortran).
 
@@ -26,15 +27,7 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #include <limits.h>
 
-#if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY)
-#  include <sys/time.h>
-#elif defined(HAVE_TIME_H)
-#  include <time.h>
-#  define TCK 1
-#else
-#define TCK 0
-#endif
-
+#include "time_1.h"
 
 extern void system_clock_4 (GFC_INTEGER_4 *, GFC_INTEGER_4 *, GFC_INTEGER_4 *);
 export_proto(system_clock_4);
@@ -52,21 +45,21 @@  void
 system_clock_4(GFC_INTEGER_4 *count, GFC_INTEGER_4 *count_rate,
 	       GFC_INTEGER_4 *count_max)
 {
+#undef TCK
+#define TCK 1000
   GFC_INTEGER_4 cnt;
   GFC_INTEGER_4 mx;
 
-#if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY)
-#undef TCK
-#define TCK 1000
-  struct timeval tp1;
+  time_t secs;
+  long nanosecs;
 
-  if (sizeof (tp1.tv_sec) < sizeof (GFC_INTEGER_4))
-    internal_error (NULL, "tv_sec too small");
+  if (sizeof (secs) < sizeof (GFC_INTEGER_4))
+    internal_error (NULL, "secs too small");
 
-  if (gettimeofday(&tp1, NULL) == 0)
+  if (gf_gettime (GF_CLOCK_MONOTONIC, &secs, &nanosecs) == 0)
     {
-      GFC_UINTEGER_4 ucnt = (GFC_UINTEGER_4) tp1.tv_sec * TCK;
-      ucnt += (tp1.tv_usec + 500000 / TCK) / (1000000 / TCK);
+      GFC_UINTEGER_4 ucnt = (GFC_UINTEGER_4) secs * TCK;
+      ucnt += (nanosecs + 500000000 / TCK) / (1000000000 / TCK);
       if (ucnt > GFC_INTEGER_4_HUGE)
 	cnt = ucnt - GFC_INTEGER_4_HUGE - 1;
       else
@@ -83,22 +76,7 @@  system_clock_4(GFC_INTEGER_4 *count, GFC_INTEGER_4 *count_rate,
 	*count_max = 0;
       return;
     }
-#elif defined(HAVE_TIME_H)
-  GFC_UINTEGER_4 ucnt;
-
-  if (sizeof (time_t) < sizeof (GFC_INTEGER_4))
-    internal_error (NULL, "time_t too small");
 
-  ucnt = time (NULL);
-  if (ucnt > GFC_INTEGER_4_HUGE)
-    cnt = ucnt - GFC_INTEGER_4_HUGE - 1;
-  else
-    cnt = ucnt;
-  mx = GFC_INTEGER_4_HUGE;
-#else
-  cnt = - GFC_INTEGER_4_HUGE;
-  mx = 0;
-#endif
   if (count != NULL)
     *count = cnt;
   if (count_rate != NULL)
@@ -114,39 +92,26 @@  void
 system_clock_8 (GFC_INTEGER_8 *count, GFC_INTEGER_8 *count_rate,
 		GFC_INTEGER_8 *count_max)
 {
+#undef TCK
+#define TCK 1000000000
   GFC_INTEGER_8 cnt;
   GFC_INTEGER_8 mx;
 
-#if defined(HAVE_SYS_TIME_H) && defined(HAVE_GETTIMEOFDAY)
-#undef TCK
-#define TCK 1000000
-  struct timeval tp1;
+  time_t secs;
+  long nanosecs;
 
-  if (sizeof (tp1.tv_sec) < sizeof (GFC_INTEGER_4))
-    internal_error (NULL, "tv_sec too small");
+  if (sizeof (secs) < sizeof (GFC_INTEGER_4))
+    internal_error (NULL, "secs too small");
 
-  if (gettimeofday(&tp1, NULL) == 0)
+  if (gf_gettime (GF_CLOCK_MONOTONIC, &secs, &nanosecs) == 0)
     {
-      if (sizeof (tp1.tv_sec) < sizeof (GFC_INTEGER_8))
-	{
-	  GFC_UINTEGER_4 ucnt = (GFC_UINTEGER_4) tp1.tv_sec * TCK;
-	  ucnt += (tp1.tv_usec + 500000 / TCK) / (1000000 / TCK);
-	  if (ucnt > GFC_INTEGER_4_HUGE)
-	    cnt = ucnt - GFC_INTEGER_4_HUGE - 1;
-	  else
-	    cnt = ucnt;
-	  mx = GFC_INTEGER_4_HUGE;
-	}
+      GFC_UINTEGER_8 ucnt = (GFC_UINTEGER_8) secs * TCK;
+      ucnt += (nanosecs + 500000000 / TCK) / (1000000000 / TCK);
+      if (ucnt > GFC_INTEGER_8_HUGE)
+	cnt = ucnt - GFC_INTEGER_8_HUGE - 1;
       else
-	{
-	  GFC_UINTEGER_8 ucnt = (GFC_UINTEGER_8) tp1.tv_sec * TCK;
-	  ucnt += (tp1.tv_usec + 500000 / TCK) / (1000000 / TCK);
-	  if (ucnt > GFC_INTEGER_8_HUGE)
-	    cnt = ucnt - GFC_INTEGER_8_HUGE - 1;
-	  else
-	    cnt = ucnt;
-	  mx = GFC_INTEGER_8_HUGE;
-	}
+	cnt = ucnt;
+      mx = GFC_INTEGER_8_HUGE;
     }
   else
     {
@@ -159,31 +124,7 @@  system_clock_8 (GFC_INTEGER_8 *count, GFC_INTEGER_8 *count_rate,
 
       return;
     }
-#elif defined(HAVE_TIME_H)
-  if (sizeof (time_t) < sizeof (GFC_INTEGER_4))
-    internal_error (NULL, "time_t too small");
-  else if (sizeof (time_t) == sizeof (GFC_INTEGER_4))
-    {
-      GFC_UINTEGER_4 ucnt = time (NULL);
-      if (ucnt > GFC_INTEGER_4_HUGE)
-	cnt = ucnt - GFC_INTEGER_4_HUGE - 1;
-      else
-	cnt = ucnt;
-      mx = GFC_INTEGER_4_HUGE;
-    }
-  else
-    {
-      GFC_UINTEGER_8 ucnt = time (NULL);
-      if (ucnt > GFC_INTEGER_8_HUGE)
-	cnt = ucnt - GFC_INTEGER_8_HUGE - 1;
-      else
-	cnt = ucnt;
-      mx = GFC_INTEGER_8_HUGE;
-    }
-#else
-  cnt = - GFC_INTEGER_8_HUGE;
-  mx = 0;
-#endif
+
   if (count != NULL)
     *count = cnt;
   if (count_rate != NULL)
diff --git a/libgfortran/intrinsics/time_1.h b/libgfortran/intrinsics/time_1.h
index 03e14ed..5ee5d5a 100644
--- a/libgfortran/intrinsics/time_1.h
+++ b/libgfortran/intrinsics/time_1.h
@@ -1,7 +1,7 @@ 
-/* Implementation of the CPU_TIME intrinsic.
-   Copyright (C) 2003, 2007, 2009 Free Software Foundation, Inc.
+/* Wrappers for platform timing functions.
+   Copyright (C) 2003, 2007, 2009, 2011 Free Software Foundation, Inc.
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -60,16 +60,38 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #  include <sys/resource.h>
 #endif  /* HAVE_GETRUSAGE && HAVE_SYS_RESOURCE_H  */
 
+/* The most accurate way to get the CPU time is getrusage ().
+   If we have times(), that's good enough, too.  */
+#if !defined (HAVE_GETRUSAGE) || !defined (HAVE_SYS_RESOURCE_H)
+/* For times(), we _must_ know the number of clock ticks per second.  */
+#  if defined (HAVE_TIMES) && (defined (HZ) || defined (_SC_CLK_TCK) || defined (CLK_TCK))
+#    ifdef HAVE_SYS_PARAM_H
+#      include <sys/param.h>
+#    endif
+#    if defined (HAVE_SYS_TIMES_H)
+#      include <sys/times.h>
+#    endif
+#    ifndef HZ
+#      if defined _SC_CLK_TCK
+#        define HZ  sysconf(_SC_CLK_TCK)
+#      else
+#        define HZ  CLK_TCK
+#      endif
+#    endif
+#  endif  /* HAVE_TIMES etc.  */
+#endif  /* !HAVE_GETRUSAGE || !HAVE_SYS_RESOURCE_H  */
+
+
 #if defined (__GNUC__) && (__GNUC__ >= 3)
 #  define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__))
 #else
 #  define ATTRIBUTE_ALWAYS_INLINE
 #endif
 
-static inline int __time_1 (long *, long *, long *, long *) ATTRIBUTE_ALWAYS_INLINE;
+static inline int gf_cputime (long *, long *, long *, long *) ATTRIBUTE_ALWAYS_INLINE;
 
 /* Helper function for the actual implementation of the DTIME, ETIME and
-   CPU_TIME intrinsics.  Returns a CPU time in microseconds or -1 if no
+   CPU_TIME intrinsics.  Returns 0 for success or -1 if no
    CPU time could be computed.  */
 
 #ifdef __MINGW32__
@@ -78,7 +100,7 @@  static inline int __time_1 (long *, long *, long *, long *) ATTRIBUTE_ALWAYS_INL
 #include <windows.h>
 
 static int
-__time_1 (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
+gf_cputime (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
 {
   union {
     FILETIME ft;
@@ -112,23 +134,37 @@  __time_1 (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
 #else
 
 static inline int
-__time_1 (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
+gf_cputime (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
 {
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_SYS_RESOURCE_H)
   struct rusage usage;
-  getrusage (RUSAGE_SELF, &usage);
+  int err;
+  err = getrusage (RUSAGE_SELF, &usage);
 
   *user_sec = usage.ru_utime.tv_sec;
   *user_usec = usage.ru_utime.tv_usec;
   *system_sec = usage.ru_stime.tv_sec;
   *system_usec = usage.ru_stime.tv_usec;
+  return err;
+
+#elif defined HAVE_TIMES
+  struct tms buf;
+  clock_t err;
+  err = times (&buf);
+  *user_sec = buf.tms_utime / HZ;
+  *user_usec = buf.tms_utime % HZ * (1000000 / HZ);
+  *system_sec = buf.tms_stime / HZ;
+  *system_usec = buf.tms_stime % HZ * (1000000 / HZ);
+  if ((err == (clock_t) -1) && errno != 0)
+    return -1;
   return 0;
 
-#else /* ! HAVE_GETRUSAGE || ! HAVE_SYS_RESOURCE_H  */
+#else 
 
   /* We have nothing to go on.  Return -1.  */
   *user_sec = *system_sec = 0;
   *user_usec = *system_usec = 0;
+  errno = ENOSYS;
   return -1;
 
 #endif
@@ -137,4 +173,71 @@  __time_1 (long *user_sec, long *user_usec, long *system_sec, long *system_usec)
 #endif
 
 
+/* POSIX states that CLOCK_REALTIME must be present if clock_gettime
+   is available, others are optional.  */
+#ifdef CLOCK_REALTIME
+#define GF_CLOCK_REALTIME CLOCK_REALTIME
+#else
+#define GF_CLOCK_REALTIME 0
+#endif
+
+#ifdef CLOCK_MONOTONIC
+#define GF_CLOCK_MONOTONIC CLOCK_MONOTONIC
+#else
+#define GF_CLOCK_REALTIME GF_CLOCK_REALTIME
+#endif
+
+/* Arguments:
+   clock_id - INPUT, must be either GF_CLOCK_REALTIME or GF_CLOCK_MONOTONIC
+   secs     - OUTPUT, seconds
+   nanosecs - OUTPUT, OPTIONAL, nanoseconds
+
+   If clock_id equals GF_CLOCK_REALTIME, the OUTPUT arguments shall be
+   the number of seconds and nanoseconds since the Epoch. If clock_id
+   equals GF_CLOCK_MONOTONIC, and if the target supports it, the
+   OUTPUT arguments represent a monotonically incrementing clock
+   starting from some unspecified time in the past.
+
+   Return value: 0 for success, -1 for error. In case of error, errno
+   is set.
+*/
+static inline int
+gf_gettime (int clock_id __attribute__((unused)), time_t * secs, 
+            long * nanosecs)
+{
+#ifdef HAVE_CLOCK_GETTIME
+  struct timespec ts;
+  int err;
+  err = clock_gettime (clock_id, &ts);
+  *secs = ts.tv_sec;
+  if (nanosecs)
+    *nanosecs = ts.tv_nsec;
+  return err;
+#elif HAVE_GETTIMEOFDAY
+  struct timeval tv;
+  int err;
+  err = gettimeofday (&tv, NULL);
+  *secs = tv.tv_sec;
+  if (nanosecs)
+    *nanosecs = tv.tv_usec * 1000;
+  return err;
+#elif HAVE_TIME
+  time_t t, t2;
+  t = time (&t2);
+  *secs = t2;
+  if (nanosecs)
+    *nanosecs = 0;
+  if (t == ((time_t)-1))
+    return -1;
+  return 0;
+#else
+  *secs = 0;
+  if (nanosecs)
+    *nanosecs = 0;
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
 #endif /* LIBGFORTRAN_TIME_H */