メモリを意識したRubyプログラミング(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Memory Conscious Programming in Ruby 原文公開日: 2017/10/31 著者: Thomas Leitner サイト: https://gettalong.org/index.html メモリを意識したRubyプログラミング(翻訳) — メモリ使用量を節約する方法と戦略 Rubyのプログラミングでメモリが大量に消費されるのは当たり前で、避けられないと考えている人がたくさんいますが、メモリ使用量を削減するさまざまな方法や戦略が利用できます。本記事ではその中からいくつかをご紹介いたします。 Rubyの内部を常に意識する TrueClass、FalseClass、NilClass、Integer、Float、Symbol、String、Array、Hash、StructといったRubyの主要なビルトインクラスは、実行パフォーマンスやメモリ使用量について高度に最適化されています。なお、ここではCRuby(MRI)を扱いますので、その他のRuby実装についてはほとんど該当しないと思われます。 Rubyの各オブジェクトへの参照は、内部(Cコード内など)ではVALUE型を経由します。これは、必要な情報をすべて含むCの構造体へのポインタです。 以下のすべての数値は64ビットLinuxプラットフォームで有効ですが、その他の64ビットシステムにも適用されるはずです。 nil、true、false、そして一部のInteger クラスによっては、オブジェクト作成時にCの構造体へのメモリ割り当てが不要なものがあります。これは、オブジェクトがVALUEで直接表現されているためです。NilClass型(nil値など)、TrueClass型(true値など)、FalseClass(false値など)のオブジェクトがこれに該当します。 -262 〜262-1の範囲に収まる小さな整数も、同様にVALUE値で表されます。 これはどういうことなのでしょうか。これらのオブジェクトは最小限のメモリで表現できるため、これらの値を用いるときにメモリを気にする必要はないということです。 オブジェクトで使われているメモリ量を返すObjectSpace.memsize_ofメソッドを使ってこのことを確認できます。 2.4.2 > require ‘objspace’ => true 2.4.2 > ObjectSpace.memsize_of(nil) => 0 2.4.2 > ObjectSpace.memsize_of(true) => 0 2.4.2 > ObjectSpace.memsize_of(false) => 0 2.4.2 > ObjectSpace.memsize_of(2**62-1) => 0 2.4.2 > ObjectSpace.memsize_of(2**62) => 40 結果からわかるように、整数の値が大きくなりすぎた最後の例を除いてメモリはまったく追加されていません。VALUE構造体が必要になると、少なくとも40バイトのメモリがオブジェクトで使われます。 Array、Struct、Hash、String これらの4クラスのオブジェクトは一般的な方法ではなく特別(に用意された専用の)C構造体を使います。値によっては、追加メモリを割り当てずにこれらの構造体に保存できます。 Arrayの要素が3つ以内であればメモリ効率が向上します。これを超えてしまうと、要素1つにつき8バイトの追加メモリが必要になります。 2.4.2 > ObjectSpace.memsize_of([]) => 40 2.4.2 > ObjectSpace.memsize_of([1]) => 40 2.4.2 > ObjectSpace.memsize_of([1, 2]) => 40 2.4.2 > ObjectSpace.memsize_of([1, 2, 3]) => 40 2.4.2 > ObjectSpace.memsize_of([1, 2, 3, 4]) => 72 Structも同様に、要素3つ以内ならメモリ効率が向上します。この場合Structのメモリは40バイトで済みます。 2.4.2 > X = Struct.new(:a, :b, :c) => X 2.4.2 > Y = Struct.new(:a, :b, :c, :d) => Y 2.4.2 > ObjectSpace.memsize_of(X.new) => 40 2.4.2 > ObjectSpace.memsize_of(Y.new) => 72 Hashの場合はやや異なります。もっとも重要なのは要素をまったく含まないハッシュは最小の40バイトで済む点です(したがってデフォルト値などで大きなペナルティは生じません)。 2.4.2 :044 > ObjectSpace.memsize_of({}) => 40 2.4.2 :045 > ObjectSpace.memsize_of({a: 1}) => 192 2.4.2 :046 > ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4}) => 192 2.4.2 :047 > ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4, e: 5}) => 288 上でわかるように、ハッシュの要素が4つ以内であれば192バイトで済みます。これが、空でないハッシュで必要な最小メモリサイズです。 最後のStringでは、23バイトまでの文字列なら文字列オブジェクトを表すRString構造体に直接保存できます。 2.4.2 :062 > ObjectSpace.memsize_of(“”) => 40 2.4.2 :063 > ObjectSpace.memsize_of(“a”*23) => … Continue reading メモリを意識したRubyプログラミング(翻訳)