0Pricing
Ruby Academy · レッスン

よくあるボトルネック

Ruby コードを高速化します。

「よくあるボトルネック」はCoddyKit上の無料Ruby Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはRuby Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Ruby Academyコースには全4レッスンが含まれています。

ボトルネックを見つける

Rubyの速度低下の多くは、いくつかの典型的なパターンから生じます。それらを知っていれば、コードをすばやく改善できます。

  • 処理に合わないデータ構造を使っている
  • キャッシュできる処理を繰り返している
  • N+1クエリやネストしたループがある

このレッスンでは、特によくある問題を取り上げます。

puts 'Speeding up Ruby starts with knowing the usual suspects'

Array#include?とSet

Array#include?でメンバーシップを確認すると、計算量はO(n)です。Set(またはハッシュ)を使うと、検索をO(1)で実行できます。

  • メンバーシップを何度も確認する場合は、最初にSetを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回の処理が発生します。

  • 一覧の取得に1回、さらに各項目につき1回クエリを実行します
  • 代わりに、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

適切なメソッドを選ぶ

Enumerableのメソッドには、メソッドチェーンよりもはるかに高速なものがあります。

  • sumはinject(:+)より高速です
  • any?は短絡評価するため、select.empty?より効率的です
  • findは最初に一致した要素で処理を止めます
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

Hashによるグループ化

group_byとtallyを使うと、何度も走査する代わりに1回の走査で集計できます。

  • tallyは出現回数を効率的に数えます
  • 手作業で数えるループが不要になります
words = ['a', 'b', 'a', 'c', 'b', 'a']
puts words.tally.inspect

文字列構築のコスト

コレクションを繰り返し連結するより、joinで結合する方が高速です。

  • 配列を作成してから、1回だけ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?の代わりにSetまたはハッシュを使い、高速にメンバーシップを確認します
  • 事前に読み込んだハッシュを使って、ネストしたループやN+1パターンをなくします
  • ||=で高コストな計算をメモ化します
  • 効率的なメソッド(sum、any?、tally)を選び、正規表現をループの外に移動します
  • 必要ないかもしれない処理は後回しにするか、スキップします

効果を確認するため、必ず実行前と実行後に計測しましょう。

よくある質問

「よくあるボトルネック」レッスンは無料ですか?

はい。「よくあるボトルネック」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Ruby Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Ruby Academyコースには全4レッスンが含まれています。

「よくあるボトルネック」で何を学びますか?

Ruby コードを高速化します。 ブラウザで直接実行するハンズオンコードでRuby Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Ruby Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのRuby Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「よくあるボトルネック」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このRuby Academyレッスンでコードを書いて実行できますか?

はい。すべてのRuby Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. パフォーマンスの測定
  2. プロファイリングツール
  3. メモリ最適化
  4. よくあるボトルネック
← Ruby Academyに戻る