2016-12-27 20 views
5

Tôi đã xem câu hỏi Why does Process.fork make stuff slower in Ruby on OS X? và có thể xác định rằng Process.fork hiện không thực sự thực hiện công việc, nói chung, chậm hơn.Tại sao `Time.utc` chậm hơn trong một quy trình chia nhỏ trong Ruby trên OS X (và không phải bằng Python)?

Tuy nhiên, có vẻ như làm cho Time.utc, đặc biệt, chậm hơn nhiều.

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 

Dưới đây là một số kết quả:

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) 

Một đầu mối có thể là sự mất trên nơi trên OS X, trong khi trên Ubuntu, có dường như không có một sự khác biệt:

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) 

Có ai có thể giải thích sự kỳ quặc này không?

Tiếp tục điều tra:

@tadman gợi ý rằng nó có thể là một lỗi trong/OS đang X Hiện hệ điều hành MacOS, vì vậy tôi đã viết một bài kiểm tra tương tự bằng 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 

Một lần nữa, trên Ubuntu , các tiêu chí chuẩn là giống nhau cho các quy trình phân chia/chính. Trên OS X, tuy nhiên, quy trình chia đôi giờ đây hơi là nhanh hơn so với quy trình chính, là đối diện với của hành vi trong Ruby.

Điều này khiến tôi tin rằng nguồn gốc của "hình phạt ngã ba" là trong quá trình triển khai Ruby và không phải trong thời gian triển khai OS X.

+1

Nó có phải là một hình phạt cho mỗi lớp ngã ba, giống như nó sẽ được tăng gấp đôi nếu trong một ngã ba phụ? Hình phạt đó có biến mất nếu quy trình gốc thoát ra không? Điều này áp dụng bên trong các chủ đề? – tadman

+0

@tadman Câu hỏi hay. Nó không phải là một hình phạt cho mỗi lớp ngã ba; các ngã ba cấp độ tiếp theo có thời gian thực hiện chính xác giống như ngã ba đầu tiên. Tôi sẽ kiểm tra phần còn lại của kịch bản nếu tôi có thể. –

+1

Tôi đang quan sát hành vi tương tự như bạn với Ruby 2.3.3 trên macOS 10.12, nhưng không thể tái tạo nó trên Fedora. Đây có thể là một vấn đề với mã thời gian trên macOS, do đó, nó có thể là giá trị nộp một báo cáo lỗi. Tôi tin rằng điều này bị cô lập với các hàm liên quan đến thời gian vì một số lý do. – tadman

Trả lời

4

Khi nó quay ra, sự chậm lại là do xấp xỉ bằng nhau cho hai cuộc gọi hàm trong time.c, trong hàm gmtime_with_leapsecond. Hai chức năng là tzsetlocaltime_r.

Khám phá đó đưa tôi đến câu hỏi Why is tzset() a lot slower after forking on Mac OS X?, trong đó câu hỏi hiện tại có thể được cho là trùng lặp.

Có hai câu trả lời đó, không chấp nhận, mà điểm đến nguyên nhân gốc rễ liên quan đến một trong hai

  • các "async-tín hiệu an toàn" -ness của tzsetlocaltime/localtime_r, hoặc
  • của Apple sử dụng một đăng ký thông báo thụ động làm mất hiệu lực khi fork 'd.

Thực tế là sự suy giảm chỉ xảy ra trong những năm không có giây nhuận được biết đến (như phát hiện bởi người sử dụng that other guy) rõ ràng là do thực tế rằng Ruby không gọi gmtime_with_leapsecond khi nó biết rằng năm nay không có bước nhảy vọt giây .

Tôi không chắc tại sao không có sự chậm lại như vậy trong Python. Một giải thích có thể là tập lệnh thử nghiệm của tôi sử dụng forkutcnow có thể không tạo quy trình con gọi đến số tzset hoặc localtime/localtime_r.

Các vấn đề liên quan