os.times() のバグ

Python 2.5 の os.times() はバグっていて、user time や system time の正しい値が採れない。

bench.py

def f(n):
  if n <= 0:
      return 1
  else:
      return f(n-1) + f(n-2)

import os, time
t1 = os.times()
f(34)
t2 = os.times()

utime = t2[0] - t1[0]
stime = t2[1] - t1[1]
total = utime + stime

print "utime %.4f, stime %.4f, total %.4f" % (utime, stime, total)


実行結果:

$ time python bench.py
utime 38.6333, stime 0.1000, total 38.7333

real    0m23.787s
user    0m23.211s
sys     0m0.138s


これをみると分かるけど、real time は 23.8 秒しかかかってないってないのに、user time が 38.6 秒と報告されている。これが os.times() のバグ。


以下のパッチを Modules/posixmodules.c に当てて再度コンパイルすると直る。

--- Modules/posixmodule.c	2008-02-03 16:20:39.000000000 +0900
+++ Modules/posixmodule.c	2008-02-29 10:38:33.000000000 +0900
@@ -5782,25 +5782,25 @@
 static PyObject *
 posix_times(PyObject *self, PyObject *noargs)
 {
 	struct tms t;
 	clock_t c;
 	errno = 0;
 	c = times(&t);
 	if (c == (clock_t) -1)
 		return posix_error();
 	return Py_BuildValue("ddddd",
-			     (double)t.tms_utime / HZ,
-			     (double)t.tms_stime / HZ,
-			     (double)t.tms_cutime / HZ,
-			     (double)t.tms_cstime / HZ,
-			     (double)c / HZ);
+			     (double)t.tms_utime / CLK_TCK,
+			     (double)t.tms_stime / CLK_TCK,
+			     (double)t.tms_cutime / CLK_TCK,
+			     (double)t.tms_cstime / CLK_TCK,
+			     (double)c / CLK_TCK);
 }
 #endif /* not OS2 */
 #endif /* HAVE_TIMES */
 
 
 #ifdef MS_WINDOWS
 #define HAVE_TIMES	/* so the method table will pick it up */
 static PyObject *
 posix_times(PyObject *self, PyObject *noargs)
 {


実行結果:

$ time python bench.py
utime 23.1800, stime 0.0500, total 23.2300

real    0m23.487s
user    0m23.211s
sys     0m0.128s


Ya, 直ってる。


追記: 書き忘れてたけど環境は MacOS X 10.4, GCC 4.0.1。それからこのパッチよりコメントのパッチのほうがきれい。


追記2: ocean 氏のをパッチにしてみた。

--- Modules/posixmodule.c	2008-02-03 16:20:39.000000000 +0900
+++ Modules/posixmodule.c	2008-02-29 22:41:00.000000000 +0900
@@ -5749,11 +5749,17 @@
 #endif /* HAVE_SYMLINK */
 
 
 #ifdef HAVE_TIMES
 #ifndef HZ
+#if defined(CLK_TCK)
+#define HZ CLK_TCK
+#elif defined(CLOCKS_PER_SEC)
+#define HZ CLOCKS_PER_SEC
+#else
 #define HZ 60 /* Universal constant :-) */
+#endif
 #endif /* HZ */
 
 #if defined(PYCC_VACPP) && defined(PYOS_OS2)
 static long
 system_uptime(void)