0Pricing
Ruby Academy · Урок

Распространённые узкие места

Ускоряйте код 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 — локальная установка не требуется.

Все уроки этого курса

  1. Измерение производительности
  2. Инструменты профилирования
  3. Оптимизация памяти
  4. Распространённые узкие места
← Назад к Ruby Academy