为什么gmtime以这种方式实现?

时间:2010-06-28 21:40:08

标签: c time

我遇到了Minix的gmtime函数。我对从纪元以来几天计算年份数的位感兴趣。以下是这一点的内容:

http://www.raspberryginger.com/jbailey/minix/html/gmtime_8c-source.html

http://www.raspberryginger.com/jbailey/minix/html/loc__time_8h-source.html

#define EPOCH_YR 1970
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)

int year = EPOCH_YR;

while (dayno >= YEARSIZE(year)) {
    dayno -= YEARSIZE(year);
    year++;
}

看起来算法是O(n),其中n是与纪元的距离。此外,LEAPYEAR似乎必须每年单独计算 - 当前日期数十次,未来日期更多。我有以下算法做同样的事情(在这种情况下从ISO-9601纪元(公元0 = 1 BC)而不是UNIX纪元):

#define CYCLE_1   365
#define CYCLE_4   (CYCLE_1   *  4 + 1)
#define CYCLE_100 (CYCLE_4   * 25 - 1)
#define CYCLE_400 (CYCLE_100 *  4 + 1)

year += 400 * (dayno / CYCLE_400)
dayno = dayno % CYCLE_400

year += 100 * (dayno / CYCLE_100)
dayno = dayno % CYCLE_100

year +=   4 * (dayno / CYCLE_4)
dayno = dayno % CYCLE_4

year +=   1 * (dayno / CYCLE_1)
dayno = dayno % CYCLE_1

对于任何日期,它在O(1)中运行,看起来即使对于接近1970年的日期也应该更快。

所以,假设Minix开发人员是聪明人,他们按照自己的方式做了一个理由,并且可能比我更了解C,为什么?

4 个答案:

答案 0 :(得分:6)

将你的代码作为y2 minix代码运行为y1 Solaris 9 v245&得到了这个分析器数据:

 %Time Seconds Cumsecs  #Calls   msec/call  Name
  79.1    0.34    0.34   36966      0.0092  _write
   7.0    0.03    0.37 1125566      0.0000  .rem
   7.0    0.03    0.40   36966      0.0008  _doprnt
   4.7    0.02    0.42 1817938      0.0000  _mcount
   2.3    0.01    0.43   36966      0.0003  y2
   0.0    0.00    0.43       4      0.      atexit
   0.0    0.00    0.43       1      0.      _exithandle
   0.0    0.00    0.43       1      0.      main
   0.0    0.00    0.43       1      0.      _fpsetsticky
   0.0    0.00    0.43       1      0.      _profil
   0.0    0.00    0.43   36966      0.0000  printf
   0.0    0.00    0.43  147864      0.0000  .div
   0.0    0.00    0.43   73932      0.0000  _ferror_unlocked
   0.0    0.00    0.43   36966      0.0000  memchr
   0.0    0.00    0.43       1      0.      _findbuf
   0.0    0.00    0.43       1      0.      _ioctl
   0.0    0.00    0.43       1      0.      _isatty
   0.0    0.00    0.43   73932      0.0000  _realbufend
   0.0    0.00    0.43   36966      0.0000  _xflsbuf
   0.0    0.00    0.43       1      0.      _setbufend
   0.0    0.00    0.43       1      0.      _setorientation
   0.0    0.00    0.43  137864      0.0000  _memcpy
   0.0    0.00    0.43       3      0.      ___errno
   0.0    0.00    0.43       1      0.      _fstat64
   0.0    0.00    0.43       1      0.      exit
   0.0    0.00    0.43   36966      0.0000  y1

也许这是一个答案

答案 1 :(得分:2)

这是纯粹的推测,但MINIX的要求可能比执行速度更重要,例如简单,易于理解和简洁?毕竟,有些代码印在教科书上。

答案 2 :(得分:1)

你的方法看起来很合理,但要让它在EPOCH_YR = 1970上运行起来有点困难,因为你现在处于几个周期的中间周期。

你能看出你是否有相同的情况,看看它是否仍然更好?

对于是否应该在任何高性能代码中使用gmtime()实现,你肯定是对的。在任何紧凑的循环中,这都是繁忙的工作。

答案 3 :(得分:0)

正确的做法。你肯定想去O(1)算法。可以在没有ado的玛雅日历中工作。检查最后一行:dayno限制为0..364,尽管在闰年它需要在0..365范围内。之前的那条线也有类似的缺陷。