メモリ最適化
割り当てを減らします。
「メモリ最適化」はCoddyKit上の無料Ruby Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはRuby Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Ruby Academyコースには全4レッスンが含まれています。
メモリが重要な理由
Rubyが生成するすべてのオブジェクトはメモリを消費し、ガベージコレクションの処理を増やします。割り当てを減らすと、プログラムを高速かつ軽量にできます。
- GCはオブジェクトを回収するために一時停止します
- 割り当て頻度が高いと、GCの実行回数が増えます
- 割り当てを減らすことが、最も大きな改善につながる場合がよくあります
before = GC.stat(:total_allocated_objects)
100.times { 'new string' + '!' }
after = GC.stat(:total_allocated_objects)
puts "Allocated: #{after - before} objects"割り当て数を数える
GC.statを使うと、ブロックの前後で割り当てカウンターを比較できます。
:total_allocated_objectsは増え続けます- 前後の差分を取って、対象部分を計測します
def allocations
before = GC.stat(:total_allocated_objects)
yield
GC.stat(:total_allocated_objects) - before
end
puts allocations { Array.new(1000) { |i| i.to_s } }文字列リテラルの凍結
同じ文字列リテラルを繰り返し使うと、そのたびに新しいオブジェクトが割り当てられます。文字列を凍結すると、Rubyは変更できない1つのインスタンスを再利用できます。
- ファイルの先頭にマジックコメントを追加します
- または、個々のリテラルに
.freezeを呼び出します
# frozen_string_literal: true
s1 = 'hello'
s2 = 'hello'
puts s1.equal?(s2)ループ内の割り当てを避ける
負荷の高いループ内でオブジェクトを作成すると、割り当てが何度も発生します。定数をループの外に移動します。
- 正規表現、配列、文字列を一度だけ作成します
- 繰り返し処理の間で再利用します
PATTERN = /\d+/
def count_numbers(lines)
lines.count { |line| line.match?(PATTERN) }
end
puts count_numbers(['a1', 'bb', 'c3'])破壊的メソッド(bang)
bangメソッドではないメソッドは新しいオブジェクトを返します。一方、bangメソッドは元のオブジェクトをその場で変更するため、割り当てを抑えられます。
map!、gsub!、sort!- 元のオブジェクトを変更しても安全な場合にだけ使用します
arr = [3, 1, 2]
arr.sort!
puts arr.inspect
s = 'hello world'
s.upcase!
puts sキーとしてのシンボルと文字列
シンボルはインターンされるため、同じシンボルは共有される1つのオブジェクトになります。ハッシュのキーにはシンボルを使用します。
- 文字列をキーにすると、使うたびに新しい文字列が割り当てられます
- 繰り返し検索する場合、シンボルによってメモリを節約できます
puts :name.equal?(:name)
puts 'name'.equal?('name')遅延列挙子
mapとselectをチェーンすると、中間配列が作成されます。lazyを使うと、要素を1つずつ処理できます。
- 中間コレクションが割り当てられません
- 大規模なシーケンスや無限シーケンスに適しています
result = (1..Float::INFINITY).lazy
.select { |n| n.even? }
.map { |n| n * n }
.first(5)
puts result.inspect読み込まずにストリーミング
ファイル全体をメモリに読み込むのは非効率的です。1行ずつストリーミングします。
each_lineは1行ずつ処理します- ファイルのサイズに関係なく、使用するメモリは一定です
text = "line1\nline2\nline3"
text.each_line do |line|
puts line.strip.upcase
end文字列の連結
ループ内で+を使って文字列を構築すると、毎回新しい文字列が割り当てられます。<<を使って、その場で追加します。
+は新しいオブジェクトを作成します<<は既存のバッファを変更します
buffer = String.new
5.times { |i| buffer << "item#{i} " }
puts bufferオブジェクトのプールと再利用
高コストなオブジェクトは作り直さずに再利用します。
- 解析済みの設定やコンパイル済みの正規表現をキャッシュします
- 定数またはメモ化した値を使用します
def config
@config ||= { host: 'localhost', port: 4567 }
end
puts config.equal?(config)GCを実行して効果を確認
GCの動作を調べて、改善を確認できます。
GC.stat(:count)は、GCが実行された回数を示します- 最適化後に実行回数が減っていれば、よい兆候です
start = GC.stat(:count)
50_000.times { |i| [i] }
puts "GC runs: #{GC.stat(:count) - start}"クイックチェック
メモリ最適化についての知識を確認しましょう。
振り返り
メモリを最適化する方法を学びました。
GC.statで割り当て数を数えます- 文字列リテラルを凍結し、オブジェクトを再利用します
- 新しいオブジェクトの作成を避けるため、bangメソッドと
<<を優先します - キーにはシンボルを、パイプラインには遅延列挙子を使用します
- 大きなデータはすべて読み込まず、ストリーミングします
次は、よくあるパフォーマンスのボトルネックに取り組みます。
よくある質問
「メモリ最適化」レッスンは無料ですか?
はい。「メモリ最適化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Ruby Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Ruby Academyコースには全4レッスンが含まれています。
「メモリ最適化」で何を学びますか?
割り当てを減らします。 ブラウザで直接実行するハンズオンコードでRuby Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Ruby Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのRuby Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「メモリ最適化」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このRuby Academyレッスンでコードを書いて実行できますか?
はい。すべてのRuby Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。