为什么在OS X上的Ruby(而不是Python)中的分叉进程中`Time.utc`会变慢?

我看到了一个问题为什么Process.fork在OS X上的Ruby中变慢了? 并且能够确定Process.fork实际上不会使任务变得更慢。

但是,它似乎确实使Time.utc慢得多。

 require 'benchmark' def do_stuff 50000.times { Time.utc(2016) } end puts "main: #{Benchmark.measure { do_stuff }}" Process.fork do puts "fork: #{Benchmark.measure { do_stuff }}" end 

以下是一些结果:

 main: 0.100000 0.000000 0.100000 ( 0.103762) fork: 0.530000 3.210000 3.740000 ( 3.765203) main: 0.100000 0.000000 0.100000 ( 0.104218) fork: 0.540000 3.280000 3.820000 ( 3.858817) main: 0.100000 0.000000 0.100000 ( 0.102956) fork: 0.520000 3.280000 3.800000 ( 3.831084) 

一个线索可能是上面发生在OS X上,而在Ubuntu上,似乎没有区别:

 main: 0.100000 0.070000 0.170000 ( 0.166505) fork: 0.090000 0.070000 0.160000 ( 0.169578) main: 0.090000 0.080000 0.170000 ( 0.167889) fork: 0.100000 0.060000 0.160000 ( 0.169160) main: 0.100000 0.070000 0.170000 ( 0.170839) fork: 0.100000 0.070000 0.170000 ( 0.176146) 

任何人都能解释这种奇怪吗?

进一步的调查:

@tadman建议它可能是macOS / OS X时间码中的一个bug,所以我在Python中编写了一个类似的测试:

 from timeit import timeit from os import fork print timeit("datetime.datetime.utcnow()", setup="import datetime") if fork() == 0: print timeit("datetime.datetime.utcnow()", setup="import datetime") else: pass 

同样,在Ubuntu上,forked / main进程的基准是相同的。 但是,在OS X上,分叉进程现在比主进程略 ,这 Ruby中的行为相反

这让我相信“fork惩罚”的来源是在Ruby实现中,而不是在OS X时间实现中。

事实certificate,在函数gmtime_with_leapsecond ,减速时间与time.c中的两个函数调用大致相同。 这两个函数是tzsetlocaltime_r

这个发现让我想到了为什么在Mac OS X上分叉后tzset()的速度要慢得多? ,其中当前的问题可能合理地说是重复的。

那里有两个答案,既没有被接受,也指出了涉及其中任何一个的根本原因

  • tzsetlocaltime / localtime_r的“异步信号安全”,或
  • Apple使用被动通知注册表,在fork’d时无效。

事实上,减速只发生在几年没有已知的闰秒(由用户发现其他人 ),这显然是因为当Ruby知道年份没有闰秒时,它不会调用gmtime_with_leapsecond

我不确定为什么Python中没有这种减速。 一种可能的解释是我使用forkutcnow测试脚本可能没有创建调用tzsetlocaltime / localtime_r的子进程。