Распространённые узкие места
Ускоряйте код Ruby
«Распространённые узкие места» — бесплатный урок Ruby Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Ruby Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Ruby Academy содержит 4 уроков всего.
Распознавание узких мест
Большинство замедлений Ruby вызвано несколькими повторяющимися шаблонами. Знание этих шаблонов позволяет быстро исправлять код.
- Неподходящая для задачи структура данных
- Повторяющаяся работа, результат которой можно кэшировать
- Запросы N+1 и вложенные циклы
На этом уроке рассматриваются наиболее распространённые причины замедления.
puts 'Speeding up Ruby starts with knowing the usual suspects'Array#include? и множество
Проверка принадлежности с помощью Array#include? имеет сложность O(n). Множество (или хеш) обеспечивает поиск за O(1).
- При многократных проверках принадлежности создайте множество один раз
- На больших коллекциях это значительно ускоряет работу
require 'set'
allowed = Set.new(['a', 'b', 'c'])
puts allowed.include?('b')
puts allowed.include?('z')Вложенные циклы (O(n в квадрате))
Сравнение каждой пары элементов имеет квадратичную сложность и резко замедляется с ростом размера.
- Замените внутренние циклы поиском в хеше
- Это превращает O(n в квадрате) в O(n)
a = [1, 2, 3, 4]
b = [3, 4, 5]
set_b = b.to_set rescue require('set') || b.to_set
common = a.select { |x| set_b.include?(x) }
puts common.inspectШаблон N+1
Получение связанных данных внутри цикла приводит к N+1 операциям.
- Один запрос для списка, затем по одному для каждого элемента
- Вместо этого объедините поисковые операции в один групповой запрос
# Bad: lookup inside loop
orders = [1, 2, 3]
prices = { 1 => 10, 2 => 20, 3 => 30 }
# Good: one preloaded hash, O(1) per item
total = orders.sum { |id| prices[id] }
puts totalМемоизация
Повторный расчёт одного и того же ресурсоёмкого значения отнимает время. Мемоизируйте его с помощью ||=.
- Кэшируйте результат после первого вычисления
- Последующие вызовы возвращают его мгновенно
class Report
def total
@total ||= begin
puts 'computing...'
(1..1000).sum
end
end
end
r = Report.new
puts r.total
puts r.totalВыбор подходящего метода
Некоторые методы перечислений значительно быстрее цепочек вызовов.
sumбыстрее, чемinject(:+)any?сразу прекращает обход, в отличие отselect.empty?findостанавливается на совпадении first
nums = (1..1_000_000)
puts nums.any? { |n| n > 5 }
puts nums.find { |n| n > 5 }Избегайте повторной сортировки
Сортировка внутри цикла или многократная сортировка обходится дорого. Отсортируйте данные один раз и повторно используйте результат.
- Сортировка имеет сложность O(n log n)
- Кэшируйте отсортированный результат, если данные не меняются
data = [5, 2, 8, 1, 9]
sorted = data.sort
puts sorted.first
puts sorted.lastГруппировка с помощью хеша
group_by и tally выполняют агрегацию за один проход вместо повторного сканирования.
tallyэффективно подсчитывает количество вхождений- Это избавляет от циклов с ручным подсчётом
words = ['a', 'b', 'a', 'c', 'b', 'a']
puts words.tally.inspectСтоимость построения строк
Объединение с помощью join для коллекций выполняется быстрее, чем многократная конкатенация.
- Сначала создайте массив, затем один раз вызовите
join - Это предотвращает создание множества промежуточных строк
parts = (1..5).map { |i| "item#{i}" }
puts parts.join(', ')Компиляция регулярных выражений
Литерал регулярного выражения внутри часто выполняемого цикла неявно компилируется заново. Вынесите его в константу.
- Компилируйте один раз, сопоставляйте множество раз
- Используйте
match?, если Вам нужно только логическое значение
EMAIL = /\A[^@\s]+@[^@\s]+\z/
inputs = ['a@b.com', 'nope', 'x@y.org']
valid = inputs.select { |s| EMAIL.match?(s) }
puts valid.inspectОтложенное выполнение
Не вычисляйте то, что может никогда не понадобиться. Откладывайте вычисления с помощью ленивой обработки или защитных условий.
- По возможности возвращайте результат раньше
- Вычисляйте ресурсоёмкие значения только по требованию
def describe(items)
return 'empty' if items.empty?
"#{items.size} items, first is #{items.first}"
end
puts describe([])
puts describe([10, 20])Быстрая проверка
Проверьте свои знания об узких местах производительности.
Повторение
Вы научились устранять распространённые узкие места:
- Использовать множество или хеш для быстрой проверки принадлежности вместо
include? - Устранять вложенные циклы и шаблоны N+1 с помощью заранее загруженных хешей
- Мемоизировать ресурсоёмкие вычисления с помощью
||= - Выбирать эффективные методы (
sum,any?,tally) и выносить регулярные выражения - Откладывать или пропускать работу, которая может никогда не понадобиться
Всегда измеряйте результат до и после, чтобы подтвердить улучшение.
Часто задаваемые вопросы
Урок «Распространённые узкие места» бесплатный?
Да — полный текст урока «Распространённые узкие места» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Ruby Academy, подпишись на CoddyKit PRO. Курс Ruby Academy содержит 4 уроков всего.
Чему я научусь в уроке «Распространённые узкие места»?
Ускоряйте код Ruby Ты практикуешь Ruby Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Ruby Academy?
Предыдущий опыт не требуется. Ruby Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Распространённые узкие места»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Ruby Academy?
Да. Каждый урок Ruby Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Измерение производительности
- Инструменты профилирования
- Оптимизация памяти
- Распространённые узкие места