2016-03-04 7 views
2

Я бегу Рубин 2.2 в RubyMine 8.0.3доли миллисекунды в Рубине

Моя машина работает Windows 7 Pro с ядром Intel i7-4710MQ

Я был в состоянии достичь ~ 411 ns с C++, Java, Python и JS на этой машине, но, похоже, не может найти способ достичь этой производительности в Ruby, поскольку встроенная библиотека Time хороша только для ms.

Я могу запрограммировать свои тесты, чтобы вытерпеть эту уменьшенную точность, но можно ли включить API QPC Windows для улучшения оценки времени выполнения?

Мой тестовый код для определения точности часы тик ниже:

numTimes = 10000 
times = Array.new(numTimes) 

(0...(numTimes)).each do |i| 
    times[i] = Time.new 
end 

durations = [] 
(0...(numTimes - 1)).each do |i| 
    durations[i] = times[i+1] - times[i] 
end 

# Output duration only if the clock ticked over  
durations.each do |duration| 
    if duration != 0 
    p duration.to_s + ',' 
    end 
end 

Код ниже включает в себя QPC, как нашел here

require "Win32API" 

QueryPerformanceCounter = Win32API.new("kernel32", 
             "QueryPerformanceCounter", 'P', 'I') 
QueryPerformanceFrequency = Win32API.new("kernel32", 
             "QueryPerformanceFrequency", 'P', 'I') 

def get_ticks 
    tick = ' ' * 8 
    get_ticks = QueryPerformanceCounter.call(tick) 
    tick.unpack('q')[0] 
end 

def get_freq 
    freq = ' ' * 8 
    get_freq = QueryPerformanceFrequency.call(freq) 
    freq.unpack('q')[0] 
end 

def get_time_diff(a, b) 
    # This function takes two QPC ticks 
    (b - a).abs.to_f/(get_freq) 
end 

numTimes = 10000 
times = Array.new(numTimes) 

(0...(numTimes)).each do |i| 
    times[i] = get_ticks 
end 

durations = [] 
(0...(numTimes - 1)).each do |i| 
    durations[i] = get_time_diff(times[i+1], times[i]) 
end 

durations.each do |duration| 
    p (duration * 1000000000).to_s + ',' 
end 

Этот код возвращает длительностей между клещами ~ 22-75 микросекунд на моей машине

+0

Что вы пытаетесь достичь с помощью этого кода? Бенчмаркинг? Кроме того, я думаю, что у вас синтаксическая ошибка. 'if duration u! = 0' – Schwern

+0

Хороший улов, должен был проскользнуть, пока я редактировал представление. Я использую этот код времени для проверки производительности таймера. Я буду использовать таймер для измерения продолжительности инициализации матрицы и умножения для различных размеров NxN. –

+0

Считаете ли вы использование [Benchmark] (http://ruby-doc.org/stdlib-2.3.0/libdoc/benchmark/rdoc/Benchmark.html)? – Schwern

ответ

1

Time#nsec

numTimes = 10000 
times = Array.new(numTimes) 

(0...(numTimes)).each do |i| 
    # nsec    ⇓⇓⇓⇓ 
    times[i] = Time.new.nsec 
end 

durations = (0...(numTimes - 1)).inject([]) do |memo, i| 
    memo << times[i+1] - times[i] 
end 

puts durations.reject(&:zero?).join $/ 
+0

Какие результаты вы получаете на своей машине с помощью этого кода? Mine возвращает 1 мс для всех ненулевых периодов –

+0

MacOS? Я получаю что-то вроде '[253, 430, 286, ...]'. – mudasobwa

+0

Хорошо, так что это поведение, которое я вижу с Time.new, конечно, связано с ОС, я получаю 1000000 для каждого значения (1 мс). Я не мог найти ничего лучше, чем использовать QPC так, как я сделал выше для Windows. –

1

Ruby Time objects хранить количество наносекунд с эпохи.

Начиная с Ruby 1.9.2, реализация времени использует подписанное 63-битное целое число, Bignum или Rational. Целое число - это число наносекунд с эпохи, которое может составлять 1823-11-12 до 2116-02-20.

Наиболее точно вы можете получить наносекундную часть с помощью Time#nsec.

$ ruby -e 't1 = Time.now; puts t1.to_f; puts t1.nsec' 
1457079791.351686 
351686000 

Как вы можете видеть, на моей машине с ОС X это только точная до микросекунды. Это может быть связано с тем, что OS X не хватает clock_gettime().

+0

В операционной системе _robust_, например. linux, результат в порядке: 'Time.now.nsec # ⇒ 526952060'. – mudasobwa

+0

Время имеет субнаносекундную точность при использовании Bignum или Rational. – Stefan

+0

Шверн, я понимаю, что работа этих функций синхронизации может немного отличаться в зависимости от того, что поддерживает базовая ОС. Я использовал массив длительностей, чтобы зафиксировать записанные времена как можно быстрее, чтобы обнаружить разрешение таймера, посмотрев на среднюю продолжительность между измерениями –

2

Вы можете получить более высокую точность при использовании Process::clock_gettime:

Возвращает время, возвращенное POSIX clock_gettime() функции.

Вот пример с Time.now

times = Array.new(1000) { Time.now } 
durations = times.each_cons(2).map { |a, b| b - a } 

durations.sort.group_by(&:itself).each do |time, elements| 
    printf("%5d ns x %d\n", time * 1_000_000_000, elements.count) 
end 

Выход:

0 ns x 686 
1000 ns x 296 
2000 ns x 12 
3000 ns x 2 
12000 ns x 2 
18000 ns x 1 

А вот тот же пример с Process.clock_gettime:

times = Array.new(1000) { Process.clock_gettime(Process::CLOCK_MONOTONIC) } 

Выход:

163 ns x 1 
    164 ns x 1 
    164 ns x 9 
    165 ns x 6 
    165 ns x 22 
    166 ns x 39 
    166 ns x 174 
    167 ns x 13 
    167 ns x 129 
    168 ns x 95 
    168 ns x 32 
    169 ns x 203 
    169 ns x 141 
    170 ns x 23 
    170 ns x 37 
    171 ns x 30 
    171 ns x 3 
    172 ns x 24 
    172 ns x 10 
    174 ns x 1 
    175 ns x 2 
    180 ns x 1 
    194 ns x 1 
    273 ns x 1 
2565 ns x 1 

А вот быстрый бок о бок сравнения:

array = Array.new(12) { [Time.now, Process.clock_gettime(Process::CLOCK_MONOTONIC)] } 

array.shift(2)    # first elements are always inaccuate 
base_t, base_p = array.first # baseline 

printf("%-11.11s %-11.11s\n", 'Time.now', 'Process.clock_gettime') 
array.each do |t, p| 
    printf("%.9f %.9f\n", t - base_t, p - base_p) 
end 

Выход:

Time.now Process.clo 
0.000000000 0.000000000 
0.000000000 0.000000495 
0.000001000 0.000000985 
0.000001000 0.000001472 
0.000002000 0.000001960 
0.000002000 0.000002448 
0.000003000 0.000002937 
0.000003000 0.000003425 
0.000004000 0.000003914 
0.000004000 0.000004403 

Это рубин 2,3 на OS X работает на i7 Intel Core, не уверен Окна.

Чтобы избежать потери точности из-за плавающей точкой преобразования, вы можете указать другую единицу, например .:

Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) 
#=> 191519383463873 
+1

'Process.clock_gettime (Process :: CLOCK_MONOTONIC)' возвращает число Float секунд, которое не имеет достаточной точности для действительно точных наносекунд. Чтобы быть точным, используйте 'Process.clock_gettime (Process :: CLOCK_MONOTONIC,: nanosecond)', который вернет Fixnum наносекунд. – Schwern

+0

@ Шварн ты уверен? Я не вижу потери точности из-за поплавков. – Stefan

+0

Возможно. В документах указано: 'Основная функция, :: clock_gettime(), возвращает несколько наносекунд.Объект Float (IEEE 754 double) недостаточно для представления возвращаемого значения для CLOCK_REALTIME. Если требуется точное значение наносекунд, используйте: наносекунды в качестве единицы. Может быть, 'CLOCK_MONOTONIC' может сойти с рук, но я бы не рискнул. [Тестирование на моей машине показывает, что это правильно на краю наносекундной точности] (https://gist.github.com/schwern/32b809c1eec1487d5506), но кто знает для другой машины? – Schwern

 Смежные вопросы

  • Нет связанных вопросов^_^