C 为什么gmtime是这样实现的?

C 为什么gmtime是这样实现的?,c,time,C,Time,我偶然发现了Minix的gmtime函数的源代码。我对从大纪元开始计算年数的比特感兴趣。以下是这一点的精髓: 看起来算法是O(n),其中n是距离历元的距离。此外,似乎每年都必须单独计算LEAPYEAR——当前日期的计算次数为几十次,未来日期的计算次数更多。我有下面的算法来做同样的事情(在本例中是从ISO-9601纪元(公元前0年=1年)而不是UNIX纪元开始的): 对于任何日期,它都以O(1)的形式运行,并且看起来它应该更快,即使对于接近1970年的日期也是如此 所以,假设Minix开发人员

我偶然发现了Minix的gmtime函数的源代码。我对从大纪元开始计算年数的比特感兴趣。以下是这一点的精髓:

看起来算法是O(n),其中n是距离历元的距离。此外,似乎每年都必须单独计算LEAPYEAR——当前日期的计算次数为几十次,未来日期的计算次数更多。我有下面的算法来做同样的事情(在本例中是从ISO-9601纪元(公元前0年=1年)而不是UNIX纪元开始的):

对于任何日期,它都以O(1)的形式运行,并且看起来它应该更快,即使对于接近1970年的日期也是如此


所以,假设Minix开发人员是聪明的人,他们这样做是有原因的,而且可能比我更了解C,为什么呢?

您的方法看起来不错,但要让它在EPOCH_YR=1970上运行要困难一些,因为您现在正处于几个周期的中期

你能看看你是否有一个同等的案例,看看它是否更好


您肯定是对的,gmtime()实现是否应该在任何高性能代码中使用是有争议的。在任何紧密的循环中都需要做大量繁忙的工作。

将代码作为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

也许这是一个答案

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

正确的方法。你肯定想做一个O(1)算法。可以在玛雅历法中毫无疑问地工作。检查最后一行:dayno限制为0..364,但在闰年,它需要范围为0..365。前一行也有类似的缺陷。

只需从0.1970年减去天数即可。这将是常数,可以存储为另一个#define。正如Adam所说,简单地减去偏移量就可以解决这个问题,尽管它会溢出32位时间戳。将epoch设置为公元2000年将解决这两个问题,需要在主计算前后进行一次额外操作。如果64位时间戳可用,ISO年零可能更好,因为它a)可以保存操作,b)更明显,c)轻松日历不可知的一周中的某一天。另一方面,如果你被现代纪元的32位时间戳所困扰,你可能会忘记100年和400年的周期,因为2000年是闰年,1900年和2100年超出了范围。@Thom Smith 2000年的天数(~730k)远低于2^32。这两个代码片段都与秒无关。我删掉了其他部分;它们在链接中。gmtime函数接受一个标准的UNIX时间戳,dayno就是从这个时间戳派生出来的。@Thom Smith:你可能会说“ISO年零可能更好”,但POSIX系统上的现有实践是将时间重新定义为一个具有相同历元的64位类型,因为这允许旧程序继续工作。看起来应该更快。您需要考虑您的体系结构、某些指令(如乘法)的速度以及分支预测器的性能(大多数都非常好)@jim mcnamara有一些有趣的结果。似乎循环实现在0秒内运行,而模实现在0.01秒内运行。这绝对是进行基准测试的一个案例。我敢打赌,在看到这些结果之前,询问者的代码会更快。
#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
 %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