2016-12-27 1 views
5

Я увидел вопрос Why does Process.fork make stuff slower in Ruby on OS X? и смог определить, что Process.fork делает не действительно делает задачи, в общем, медленнее.Почему `Time.utc` медленнее в разветвленном процессе в Ruby на OS X (а не на Python)?

Однако, как представляется, 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 предположил, что это может быть ошибка в/OS времени X кода MacOS, так что я написал подобный тест в 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 , контрольные показатели одинаковы для разветвленных/основных процессов. В OS X, однако, раздвоенный процесс теперь немного быстрее, чем основной процесс, который является противоположным поведением в Ruby.

Это заставляет меня думать, что источник «штрафного удара» находится в реализации Ruby, а не в реализации OS X.

+1

Это штраф за уровень вилки, как если бы он был двойным, если в другой суб-вилке? Означает ли это наказание, если родительский процесс завершается? Используется ли это внутри потоков? – tadman

+0

@tadman Прохладные вопросы. Это не штраф за уровень вилки; вилка следующего уровня имеет то же самое время выполнения, что и первая вилка. Я проверю остальные сценарии, если смогу. –

+1

Я наблюдаю то же поведение, что и с Ruby 2.3.3 на macOS 10.12, но не могу воспроизвести его на Fedora. Вероятно, это проблема с временным кодом на macOS, поэтому, возможно, стоит написать отчет об ошибке. Я считаю, что по какой-то причине это изолировано от временных функций. – tadman

ответ

4

Как выясняется, замедление происходит примерно в равной степени с двумя вызовами функций в time.c, в функции gmtime_with_leapsecond. Две функции: tzset и localtime_r.

Это открытие привело меня к вопросу Why is tzset() a lot slower after forking on Mac OS X?, из которого текущий вопрос можно было бы назвать дублирующим.

Есть два ответа там, ни приняты, которые указывают на коренные причины, связанные либо

  • «Async-сигнал безопасный» -ness из tzset и localtime/localtime_r или
  • использование Яблока реестр пассивного уведомления, который недействителен, когда fork 'd.

Тот факт, что замедление происходит только в годы с не известных високосных секунд (в обнаруженных пользователем that other guy), очевидно, связано с тем, что Руби не называют gmtime_with_leapsecond, когда он знает, что год не имеет високосных секунд ,

Я не уверен, почему такого замедления в Python не наблюдается. Одно из возможных объяснений заключается в том, что мой тестовый скрипт с использованием fork и utcnow может не создавать дочерний процесс, который вызывает tzset или localtime/localtime_r.