为什么Time.utc在Ruby on OS X(而不是Python)中的分叉进程中速度较慢?

use*_*951 7 ruby python performance fork

我看到了问题为什么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
Run Code Online (Sandbox Code Playgroud)

以下是一些结果:

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)
Run Code Online (Sandbox Code Playgroud)

一个线索可能是上面发生在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)
Run Code Online (Sandbox Code Playgroud)

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

进一步的调查:

@tadman建议它可能是macOS/OS X时间码中的一个错误,所以我在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
Run Code Online (Sandbox Code Playgroud)

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

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

use*_*951 6

事实证明,在函数中time.c,减速是由于与函数中的两个函数调用大致相等gmtime_with_leapsecond.这两个功能是tzsetlocaltime_r.

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

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

  • tzsetlocaltime/ localtime_r,或的"异步信号安全"
  • Apple使用被动通知注册表,在forkd 时无效.

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

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