よくあるボトルネック
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.inspectN+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.lastHashによるグループ化
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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- パフォーマンスの測定
- プロファイリングツール
- メモリ最適化
- よくあるボトルネック