Як визначити час роботи в мілісекундах у Ruby?


79

Я хочу з'ясувати, скільки мілісекунд використовує певна функція. Тож я виглядав високо і низько, але не міг знайти способу, щоб отримати час у Ruby з мілісекундною точністю.

Як ти це робиш? У більшості мов програмування це просто щось на зразок

start = now.milliseconds
myfunction()
end = now.milliseconds
time = end - start

Відповіді:


92

Ви можете використовувати Timeклас ruby's . Наприклад:

t1 = Time.now
# processing...
t2 = Time.now
delta = t2 - t1 # in seconds

Тепер, deltaє floatоб’єктом, і ви можете отримати такий дрібний результат, як це забезпечить клас.


2
Це просте рішення, але краще відповідь із Process.clock_gettime. Дивіться blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Guy C

60

Ви також можете використовувати вбудовану функцію Benchmark.measure:

require "benchmark"
puts(Benchmark.measure { sleep 0.5 })

Друк:

0.000000   0.000000   0.000000 (  0.501134)

1
Мені подобається ідея використання вбудованої бібліотеки, але які перші три цифри ( 0.000000)? Оновлення: розглянуто це в документації, і це час користувача процесора, час процесора системи та сума цих двох.
lyonsinbeta

1
Так. Вони позначаються, якщо ви використовуєте Benchmark.bm. Корисно подивитися, де часом витрачається час. Тут ви бачите, що блок просто холостий (0 загалом, 0,5 реальних)
Саймон Перепелиця

13
Або є такий, Benchmark.realtimeякий повертає єдиний зрозумілий поплавок :)
Серхіо Туленцев

26

Використання Time.now(яке повертає час настінного годинника) як базових ліній має кілька проблем, які можуть призвести до несподіваної поведінки. Це спричинено тим, що час настінного годинника може зазнавати змін, таких як вставлені високосні секунди або час повороту для налаштування місцевого часу на контрольний час.

Якщо під час вимірювання вставлена, наприклад, стрибкова секунда, вона буде вимкнена на секунду. Так само, залежно від локальних системних умов, можливо, доведеться мати справу з переходом на перехід на літній час, швидше або повільніше працюючих годинників, або навіть перехід годинника назад у часі, що призводить до негативної тривалості, та багато інших проблем.

Рішенням цього питання є використання іншого часу годинника: монотонний годинник. Цей тип годинника має інші властивості, ніж настінний. Він збільшується монітонічно, тобто ніколи не повертається назад і збільшується з постійною швидкістю. При цьому він не відображає настінний годинник (тобто час, який ви читаєте з годинника на вашій стіні), але мітку часу, яку ви можете порівняти з пізньою міткою часу, щоб отримати різницю.

У Ruby ви можете використовувати таку позначку часу з Process.clock_gettime(Process::CLOCK_MONOTONIC)наступним чином:

t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63988.576809828

sleep 1.5 # do some work

t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63990.08359163

delta = t2 - t1
# => 1.5067818019961123
delta_in_milliseconds = delta * 1000
# => 1506.7818019961123

Process.clock_gettimeМетод повертає тимчасову мітку у вигляді поплавця з часткою секунди. Фактичне число, що повертається, не має визначеного значення (на яке слід покладатися). Однак ви можете бути впевнені, що наступний дзвінок поверне більшу кількість, і, порівнявши значення, ви можете отримати реальну різницю в часі.

Ці атрибути роблять метод головним кандидатом для вимірювання різниці в часі, не бачачи, щоб ваша програма провалилася в найменш сприятливий час (наприклад, опівночі напередодні Нового року, коли буде вставлена ​​чергова стрибкова секунда).

Process::CLOCK_MONOTONICКонстанта , яка використовується тут доступна на все сучасну Linux, BSD і систему MacOS, а також Linux підсистемі для Windows. Однак він ще не доступний для "необроблених" систем Windows. Там ви можете використовувати GetTickCount64системний виклик, замість Process.clock_gettimeякого також повертає значення таймера в мілісекундах деталізації в Windows (> = Windows Vista, Windows Server 2008).

За допомогою Ruby ви можете викликати цю функцію так:

require 'fiddle'

# Get a reference to the function once
GetTickCount64 = Fiddle::Function.new(
  Fiddle.dlopen('kernel32.dll')['GetTickCount64'],
  [],
  -Fiddle::TYPE_LONG_LONG # unsigned long long
)

timestamp = GetTickCount64.call / 1000.0
# => 63988.576809828

1
Це єдина відповідь, яка повинна бути прийнятною, не кажучи вже про прийняту.
Роб Кіньйон

Можливо, варто додати деякі подробиці про підтримку ОС: SUSv3 до 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12
Guy C,

Оригінальні запитання та відповіді були надані до того, як це рішення стало доступним. Чому це має бути єдиною "прийнятною" відповіддю?
CitizenX

13

Вам слід поглянути на модуль тестування для виконання тестів. Однак як швидкий і брудний метод хронометражу ви можете використовувати щось на зразок цього:

def time
  now = Time.now.to_f
  yield
  endd = Time.now.to_f
  endd - now
end

Зверніть увагу, використання Time.now.to_f, яке на відміну від to_i, не буде скорочуватися до секунд.


6
Час відбору для поплавців тут непотрібний.
Серхіо Туленцев

також, друкарська помилка в "endd"
нічний пул

@nightpool Це пишеться так, тому що endце ключове слово.
sepp2k

1
тоді робіть « end_time
лайк»


1

absolute_timeКамінь є заміною для Benchmark, але використовує власні інструкції , щоб бути більш точним.


1
або дорогоцінний камінь hitimes, який може мати вищу деталізацію на вікнах, ніж absolute_time ...
rogerdpack

1

Також ми можемо створити просту функцію для реєстрації будь-якого блоку коду:

def log_time
  start_at = Time.now

  yield if block_given?

  execution_time = (Time.now - start_at).round(2)
  puts "Execution time: #{execution_time}s"
end

log_time { sleep(2.545) } # Execution time: 2.55s

0

Якщо ви використовуєте

date = Time.now.to_i

Ви отримуєте час у секундах, що є далеко не точним, особливо якщо ви відстежуєте невеликі фрагменти коду.


0

Використання Time.now.to_iповернення другого перейшло з 01.01.1970. Знаючи це, ви можете це зробити

date1 = Time.now.to_f
date2 = Time.now.to_f
diff = date2 - date1

Завдяки цьому ви матимете різницю в другій величині. Якщо ви хочете це зробити в мілісекундах , просто додайте до коду

diff = diff * 1000

2
.to_iдає ціле число і скорочує мілісекунди. Це спрацювало б, якби ви просто змінили його на.to_f
Райан Тейлор

1
Так, як каже Райан; вибачаюся за голос проти, але насправді він мав один за, і я відчув себе змушеним протидіяти цьому. Помноживши цілу різницю в часі в секундах на 1000, щоб отримати мілісекунди? Давай, MARC.RS, напевно ти просто нас тролював! :-)
Ендрю Ходжкінсон,

Ні, просто намагаюся з усіх
сил

0

У мене дорогоцінний камінь, який може профілізувати ваш метод ruby ​​(екземпляр або клас) - https://github.com/igorkasyanchuk/benchmark_methods .

Більше такого коду немає:

t = Time.now
user.calculate_report
puts Time.now - t

Тепер ви можете зробити:

benchmark :calculate_report # in class

І просто зателефонуйте своєму методу

user.calculate_report
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.