Программирование на языке Ruby
Программирование на языке Ruby читать книгу онлайн
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Но этот пример не показывает, в чем истинная ценность профилирования. В программе нет ни методов, ни классов. На практике вы, скорее всего, увидите свои методы среди системных. И тогда будете точно знать, какие из ваших методов находятся в числе первых 20 «пожирателей времени».
Надо ясно понимать, что профилировщик Ruby (видно, по иронии судьбы) работает медленно. Он подключается к программе во многих местах и следит за ее выполнением на низком уровне (причем сам написан на чистом Ruby). Так что не удивляйтесь, если ваша программа в ходе сеанса профилирования будет работать на несколько порядков медленнее. В нашем примере она работала 7 минут 40 секунд (460 секунд), то есть в 25 раз медленнее обычного.
Помимо профилировщика, есть еще один низкоуровневый инструмент — стандартная библиотека
benchmark
Один из способов ее применения — вызвать метод
Benchmark.measure
require 'benchmark'
file = "/usr/share/dict/words"
result = Benchmark.measure { File.readlines(file) }
puts result
# Выводится: 0.350000 0.070000 0.420000 ( 0.418825)
Этот метод выводит следующую информацию:
• время, затраченное процессором в режиме пользователя (в секундах);
• время, затраченное процессором в режиме ядра (в секундах);
• полное затраченное время — сумму вышеупомянутых величин;
• время работы программы (по часам).
Для сравнения производительности отдельных участков удобен метод
Benchmark.bm
require 'benchmark'
n = 200_000
s1 = ""
s2 = ""
s3 = ""
Benchmark.bm do |rep|
rep.report("str << ") { n.times { s1 << "x" } }
rep.report("str.insert ") { n.times { s3.insert(-1,"x") } }
rep.report("str += ") { n.times { s2 += "x" } }
end
Здесь мы сравниваем три способа добавить символ в конец строки, дающие один и тот же результат. Чтобы можно было получить более точные цифры, каждая операция выполняется 200000 раз. Вот что вышло:
user system total real
str << 0.180000 0.000000 0.180000 ( 0.174697)
str.insert 0.200000 0.000000 0.200000 ( 0.200479)
str += 15.250000 13.120000 28.370000 (28.375998)
Обратите внимание, что последний вариант на два порядка медленнее остальных. Почему? Какой урок можно извлечь отсюда?
Вы можете предположить, что оператор
+
Стало быть, вывод такой: создание объекта — дорогая операция. Библиотека Benchmark может преподать много подобных уроков, но я все же рекомендую сначала заняться высокоуровневым профилированием.
16.7. Объекты печати
Метод
inspect
p
Проблема в том, что результат, формируемый методом
p
pp
my_obj
class MyClass
attr_accessor :alpha, :beta, :gamma
def initialize(a,b,c)
@alpha, @beta, @gamma = a, b, с
end
end
x = MyClass.new(2, 3, 4)
y = MyClass.new(5, 6, 7)
z = MyClass.new(7, 8, 9)
my_obj = { x => y, z => [:p, :q] }
p my_obj
Вызов метода
p
{#<MyClass:0xb7eed86c @beta=3, @alpha=2,
@gamma=4>=>#<MyClass:0xb7eed72c @beta=6, @alpha=5, @gamma=7>,
#<MyClass:0xb7eed704 @beta=8, @alpha=7 , @gamma=9>=>[:p, :q]}
Все правильно и в общем-то даже читаемо. Но… некрасиво. А давайте затребуем библиотеку
pp
pp
require 'pp'
# ...
pp my_obj
Теперь вывод приобретает такой вид:
{#<MyClass:0xb7f7a050 @alpha=7, @beta=8, @gamma=9>=>[:p, :q],
#<MyClass:0xb7f7a1b8 @alpha=2, @beta=3, @gamma=4>=>
#<MyClass:0xb7f7a078 @alpha=5, @beta=6, @gamma=7>}
Мы получили хотя бы пробелы и разбиение на строки. Уже лучше. Но можно пойти еще дальше. Предположим, что в классе
MyClass
pretty_print
class MyClass
def pretty_print(printer)
printer.text "MyClass(#@alpha, #@beta, #@gamma)"
end
end
Аргумент printer передается вызывающей программой (или методом pp). Это аккумулятор текста, являющийся экземпляром класса
PP
text
self