Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

省メモリーに関するデザインパターン 2011.04.18

10,773 views

Published on

富豪的プログラミングが当たり前となった今、特にメモリーをケチるようなプロ
グラミングの事は忘れてしまっていると思います。

ただ、iPhoneやAndroidのように小さなメモリーしか載っていない携帯端末が流
行っていることや、サーバーサイドで扱うデータ量がユーザーのニーズによって
巨大化している事などから、そろそろ省メモリなプログラミングの事も思い出し
たほうがいいのかなと私は最近思い始めています。

また、システムのパフォーマンスを支える部分にも「省メモリ」であることは有
効です。たとえば情報を転送する際、同一の情報をより小さく表現できるとそれ
だけ純粋にパフォーマンスは向上します。1つのデータを半分の表現形式でエン
コードできた場合、転送スピードは2倍になってくれます。そうなると、転送時
間が2時間かかる部分が1時間で済むのです!(場合によって)

というわけで、この勉強会では省メモリに関するデザインパターンを説明します。
最近読んだ本で「省メモリプログラミング」という本があり、この本にデザイン
パターンはまとめられていました。この中から「これは!」と思えるパターンを
抜き出し、説明します。

Published in: Software
  • Look at my new car! I am so excited! Getting a bargain at auction was easier than we thought. We will do it again. ❤❤❤ https://w.url.cn/s/AFqTUhi
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Dating direct: ♥♥♥ http://bit.ly/39sFWPG ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Dating for everyone is here: ♥♥♥ http://bit.ly/39sFWPG ♥♥♥
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download Full EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ACCESS WEBSITE for All Ebooks ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download EPUB Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... Download doc Ebook here { https://tinyurl.com/yyxo9sk7 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

省メモリーに関するデザインパターン 2011.04.18

  1. 1. ULS Copyright © 2011 UL Systems, Inc. All rights reserved. 省メモリーに関するデザインパターン 書籍:『省メモリプログラミング』より ウルシステムズ株式会社 http://www.ulsystems.co.jp mailto:info@ulsystems.co.jp Tel: 03-6220-1420 Fax: 03-6220-1402 2011年4月18日 講師役:近棟 稔
  2. 2. ULS Copyright © 2011 UL Systems, Inc. 1 今更なぜメモリーをケチるの?  背景 – iPhoneやAndroidのように小さなメモリーしか載っていない携帯端末が流行っ ています。しかし、アプリケーションに対するユーザーの要望は高くなり続けてい ます。 – サーバーサイドで扱うデータ量がユーザーのニーズによって巨大化しています。 しかし、特にクラウド系のサービス構築の場合、サーバーリソースを無尽蔵に使 ってしまうと消費電力やハードウエアコストが上がってしまいます。  携帯端末とデータセンターの面白い共通点 – 携帯端末もクラウド系サービスを支えるデータセンターも、「消費電力」がキーワ ードになっています。 – 携帯端末もクラウド系サービスを支えるデータセンターも、扱いたいデータを単純 なリニア空間のメモリー上にすべて乗せることが難しくなっています。  書籍:『省メモリプログラミング』そのものについて – 2000年11月に書かれた本ですが、高い効率を持つアプリケーションを構築する ための基本的な考え方がうまくまとめられています。今後、システムの設計を行 う上でのアイディアの元になりそうな書籍でした。
  3. 3. ULS Copyright © 2011 UL Systems, Inc. 2 省メモリプログラミングパターン一覧 カテゴリー パターン 内容 small architecture memory limit モジュール毎にquotaを設定する。(Androidの場合16MBメモリー上限あり) small interfaces Listではなくイテレータを関数の戻り値にするなどして、逐次アクセスAPIを活 用する。そうすることで、メモリー上に大きなデータを展開しなくても良くなる。 partial failure メモリー不足に陥った際は、そのモジュールのみ失敗させ、サービスそのもの は縮退モードで続行することで、ユーザビリティが向上する。 captain Oates 重要ではないモジュールは、メモリー不足状態を検知したら自らシャットダウン するように作る。例:Androidではlow-memoryブロードキャスト。(Oatesは 南極探検隊の一人) read-only memory 組み込み機器の場合、プログラムloadが必要ないROMを活用する。 hooks ROM内に収めたプログラムに対して後でパッチを当てられるようにしておく。 secondary strage application switching アプリケーション分割をし、1つ1つのプログラムサイズを小さく抑える。 data files データファイルを小さな複数ファイルに分割し、1つ1つのファイルサイズを小さ くする。例:大規模データの部分ソート+マージソートの組み合わせ resource files 画像などは必要なときにロード可能なように外部ファイルとする。 packages プログラムを多数のモジュールに分割し、ダイナミックにロード、アンロードする ようにする。windowsではdll、linuxではsoにして実現する。Javaでは普通。 paging OSの提供するスワップの機構を利用する。近代的なOSでは普通。 compression table compression ハフマン符号化などを用いた圧縮を行う。 difference coding 差分コーディングやランレングスエンコーディングを使う。 (例:androidのKeyEventは複数のイベントが1つに「圧縮」される) adaptive compression LZ圧縮やBzip2などを用いたデータ圧縮を行う。 説明するもの 説明しないもの
  4. 4. ULS Copyright © 2011 UL Systems, Inc. 3 省メモリプログラミングパターン一覧 カテゴリー パターン 内容 small data structures packed data 小さな領域にデータを詰め込む。 sharing flyweightパターンと同等。複数場所でデータを共有。 copy-on-write データの複製時には物理的なコピーをせず、変更時にはじめてコピーを 作成する方法。 embedded pointers linked listのコンテナのように、構造を形成する部分に大きな領域を必 要とする状態を改善する方法。 multiple representations 内部表現を個々のオブジェクトに合った形に最適化し、情報を最小化し ます。 memory allocations fixed allocations 初期化時にすべてのメモリー領域の確保を済まし、処理中は新たなメモ リー領域の確保を行わないようにする。 variable allocations mallocを使う。(組み込みではこれをサポートしていない場合もある) memory discard fixed allocationsパターンを部分適用し、mallocのオーバーヘッドを 最小化する。一括メモリー確保、一括メモリー開放を行う。 pooled allocation 固定サイズのオブジェクトプールからオブジェクトを貸し出すことによって、 高速なオブジェクトの生成・破棄を実現する。 compaction メモリー領域のデフラグを行う。 reference counting 参照カウント方式のガベージコレクション garbage collection マーク&スイープやコピー方式のガベージコレクション 説明するもの 説明しないもの
  5. 5. ULS Copyright © 2011 UL Systems, Inc. 4 small architecture
  6. 6. ULS Copyright © 2011 UL Systems, Inc. 5 hooks  シチュエーション – BIOSなど、ROMで提供されているルーチンを部分的に修正したり、処理の前後に自前の処理(前処理・後処理)を追加した りしたくなる場合があります。  解決策 – 割り込みベクタテーブルのような物を作り、ROM内でサブルーチンコールする際や、プログラム内でサブルーチンコールす る際にはそのベクタテーブル経由で呼び出すようなアーキテクチャにします。  採用例 – ハードウエア割り込みやソフトウエア割り込みで一般的に使われています。 – Javaなどのオブジェクト指向言語における継承によるメソッドオーバーライドはベクタテーブルの一種と考えられます。 – 関数型言語のように関数がファーストクラスである言語ではすべての関数がベクタテーブルに乗っているとも考えられます。  別名 – ベクタテーブル、ジャンプテーブル、パッチテーブル、割り込みベクタテーブル サブルーチン1 サブルーチン2 サブルーチン3 サブルーチン4 ROM内ベクタテーブル(RAM内) 機能番号 関数ポインタ 0x01 (デフォルトではROM内の処理を指す) 0x02 (デフォルトではROM内の処理を指す) 0x03 RAM内の処理に変更されている 0x04 (デフォルトではROM内の処理を指す) 利用者はベクタテーブル越しに 各種サブルーチン呼び出しをする (ルーチンの呼び先はROM内では ないかもしれない) サブルーチン3' RAM内
  7. 7. ULS Copyright © 2011 UL Systems, Inc. 6 secondary strage
  8. 8. ULS Copyright © 2011 UL Systems, Inc. 7 data files  シチュエーション – 処理対象のファイルが大きすぎて処理が出来ない事があります。  解決策 – 処理対象のファイルを処理可能な大きさに分割して処理することでうまくいくことがあります。たとえば、数 テラバイトのファイルのソート処理などです。  採用例 – UNIXのsortコマンドには上記のような分割ソートをサポートするためのコマンドラインオプションが存在し ます。 – GoogleのBigtableや。HadoopのHDFSはこのような考え方に立脚しています。 (大きなデータは分割したまま保持) – gccなどのコンパイラは通常個別ソースコードをコンパイル後、リンカによって最後に1つのプログラムモジ ュールに統合されます。 数テラバイトの データ 数メガバイトのファイル 数メガバイトのファイル 数メガバイトのファイル 数メガバイトのファイル ・ ・ ・ ソート済み部分ファイル ソート済み部分ファイル ソート済み部分ファイル ソート済み部分ファイル ・ ・ ・ ソート ソート ソート ソート 数テラバイトの ソート済み データ マージ ソートの マージ フェーズ
  9. 9. ULS Copyright © 2011 UL Systems, Inc. 8 compression
  10. 10. ULS Copyright © 2011 UL Systems, Inc. 9 difference coding  シチュエーション – 変化の少ない大量のデータを扱う場合、簡単なロジックで圧縮が可能な場合があります。  解決策 – 「差分コーディング」や「ランレングス圧縮」を用います。  採用例 – キー入力においてキーリピートが発生した場合、大量のKeyEventが発生します。このようなオブジェクトをシステム内で大 量にnewしてしまうと、オブジェクトの生成・破棄だけでも大変な負荷になってしまいます。 通常、このような事を想定し、キーリピートは「ランレングス圧縮」します。 – 例:Androidの場合 KeyEvent keyCode:int repeatCount:int KeyEvent keyCode:int KeyEvent keyCode:int KeyEvent keyCode:int ・ ・ ・ 1つにまとめる
  11. 11. ULS Copyright © 2011 UL Systems, Inc. 10 small data structures
  12. 12. ULS Copyright © 2011 UL Systems, Inc. 11 packed data  シチュエーション – ランダムアクセス性能は確保した状態で、限られたメモリーの中で可能な限り大量の情報を 扱いたい場合に使用します。 – プロジェクトでの経験 1千万件(10M件)のデータに対する高速なランダムアクセスを実現するために、Javaのメモ リー中に情報を保持することになりました。しかし、Bean1つあたりのデータサイズが1Kバイ トあると10GBのメモリー空間が必要になってしまいました。  解決策 – 対象の情報を十分格納可能な最小のデータ構造を考えます。(圧縮に関してはpacked dataの範囲外です)  手法 – 情報を表現する際に、使用する型などを工夫することによって、同じ情報を効率的に表現する 方法を考えます。 – packed dataではデータ圧縮までは考えません。圧縮までしてしまうと、高速なBeanの読み 書きが出来なくなってしまうためです。
  13. 13. ULS Copyright © 2011 UL Systems, Inc. 12 packed dataの例:Pixelクラス (Androidのピクセル表現について)  以下にPixelクラスの各種バリエーションを示します。Androidは(D)方式です。 (A) 最も無駄に作ったPixelクラス public class Pixel { Byte alpha; Byte red; Byte green; Byte blue; } Pixelオブジェクトそのもの=8バイト フィールドの4つのポインタ=8バイト * 4=32バイト (フィールド値はByte.valueOf()でキャッシュ可能) 計:40バイト! (B) 32bit色を出せれば十分と考えた場合 public class Pixel { byte alpha; byte red; byte green; byte blue; } Pixelオブジェクトそのもの=8バイト byteもint幅で確保される(!)ため=4バイト * 4=16バイト 計:24バイト! (D) クラスも不要とすると int argb; 計:4バイト! (C) int幅が最小幅なのだから、そこに詰め込み public class Pixel { int argb; } Pixelオブジェクトそのもの=8バイト argbフィールド=4バイトではなく、8バイトでした。というのも フィールド個数が奇数の場合、偶数フィールドまで確保され てしまうからです。 計:16バイト! 最初のサイズの10分の1のサイズになりました! 10Mピクセルの写真なら1枚40MBのメモリーに入ります。 元のデータ構造では400MBも必要でした。 ピクセルをintで表現する方法はAndroidで採用されています。 1倍4倍 6倍10倍 オブジェクトのサイズを知る方法 java.exe -agentlib:hprof=heap=sites pixel.Main
  14. 14. ULS Copyright © 2011 UL Systems, Inc. 13 sharing  シチュエーション – true,falseなど、同一データをがたくさん登場する事が見込まれる場合、それらのデータを別 オブジェクトにするのはもったいない場合があります。  解決策 – flyweightパターンを使って不変オブジェクトを作り、それを色々な場所で参照することでトー タルのメモリー消費を抑えます。  sharingの例 – Javaでは基本型のラッパークラスの持つvalueOfメソッドが sharingを促進するために用意されています。 – Javaを含めた各種言語には、文字列に関して internという機構があり、これを使えばsharing可能です。 – immutableなオブジェクトをシステム全体でsharing する方法はパフォーマンスを稼ぐ際に一般的な方法です。 プリミティブ型 キャッシュが使われる範 囲 boolean true, false byte 全範囲 char 0 ~127の範囲 short -128 ~ 127の範囲 int -128 ~ 127の範囲 long -128 ~ 127の範囲 float キャッシュなし double キャッシュなし
  15. 15. ULS Copyright © 2011 UL Systems, Inc. 14 sharingにおけるオブジェクト間の接続イメージ  sharingするということは、以下のようなイメージとなります。share対象のオブジェクトは immutableであった方が安全です。 :Bean num:Integer name:String flag:Boolean :Bean num:Integer name:String flag:Boolean :Bean num:Integer name:String flag:Boolean :Bean num:Integer name:String flag:Boolean :Bean num:Integer name:String flag:Boolean :Bean num:Integer name:String flag:Boolean true:Boolean false:Boolean abc:String def:String xyz:String 0:Integer 1:Integer 2:Integer sharingされたオブジェクト
  16. 16. ULS Copyright © 2011 UL Systems, Inc. 15 copy on write  シチュエーション – sharingによって複数スレッドなどから共有されている大規模データを、個別スレッド上で自由に編集 したい。しかし、個別スレッドに元データのすべてをコピーしてくるのは非効率である。 – (組込み系で)ROMデータの内容を一部編集したい。  解決策 – コピー時 物理的なコピーはせずに、コピー元のデータをそのままコピー先でsharingします。 – 編集時 コピー元とデータをsharingしたままだと編集結果が元データへ反映されてしまうため、編集時にはじめて物 理的なコピーをし、それに対する編集を行います。「書き込み時(on write)」にはじめて「コピー(copy)」す るということでcopy on writeといいます。  copy-on-writeの採用例 – Subversionはsharingとcopy-on-writeをうまく活用した例です。Subversion上のファイルコピーは、ど んな大規模データでも一瞬で完了します。この理由は、ファイルのコピー時には実際のファイルの複製を行 っておらず、元のデータを指し示す小さな情報を保存しているだけだからです。コピー後の情報を書き換える (チェックインする)場合、そこではじめて変更が生じた部分のコピーを作成して変更内容を保存します。この ような仕組みによって、Subversion上のデータは多くの部分がsharingされたままとなり、ディスクスペー スを圧迫しにくい仕組みになっています。 – Linuxカーネルをはじめとする各種OSは、プロセスのfork時にcopy-on-writeを使っています。プロセス がforkした瞬間、実際にプロセスが分裂したように見えますが、プロセス空間のコピーを実際に行なってい るわけではありません。また、この場合親プロセスのキャッシュが子プロセス側でも有効なため、キャッシュヒ ット率を上げる効果もあります。
  17. 17. ULS Copyright © 2011 UL Systems, Inc. 16 copy on writeの挙動サンプル  ツリー構造をcopy on writeによって操作します。この例はproxyによるcowの実現例です。 1 2 4 3 5 6 システム全体でsharingされているデータ 元オブジェクトをproxyオブジェクトで包みます コピー 操作 部分的に変更を施します 1 2 4 3 5 6 1 7 4 3 5 8 編集 copy on write 元のオブジェクトのまま 新オブジェクト 元のオブジェクトのまま オブジェクトツリーを操作している人からすると、 システム全体の共有データをコピーしたものは、 本当にコピーしたものに見えているし、 そのコピーデータの編集操作は自然なものに感じられる。
  18. 18. ULS Copyright © 2011 UL Systems, Inc. 17 embedded pointers  シチュエーション – コレクションクラスの構築時、linked listのような構造はデータそのものよりもlist構造を形成するノードその ものの方が領域を必要とする場合があります。その結果、「入れるものより容器のほうが大きい」といった状 況になってしまいます。しかし、ArrayListや配列を使ってしまうとデータ列の任意の場所への追加・削除コ ストが大きくなってしまいます。  解決策 – たとえばBeanを格納するlinked listであれば、listのnodeをデータであるBeanとは別に作ることをやめ、 格納対象のBeanそのものの中に次のデータを指し示すポインタを埋め込みます(下図)。 List first last Node next value Node next value Node next value Bean Bean Bean 普通の linked list ここだけで大量のメモリーを消費します。 1ノードの表現はオブジェクトが8バイト、 ポインタ2つで2*8=16バイトで 1ノードだけで計24バイトも消費します。 プロジェクトで経験した1千万件(10M件)の データであれば、ここだけで240MBも必要です。 List first last Bean next Bean next Bean next embedded pointers Beanの中にポインタを埋め込むことで、 linked listを構築するための増分は ポインタだけで済み、8バイトのみ(1/3)で良くな ります。 24 バイト 8 バイト
  19. 19. ULS Copyright © 2011 UL Systems, Inc. 18 embedded pointersの実際の設計  Javaでembedded pointersを実現する方法を以下に示します。  解説 – 各Nodeはインターフェースとして定義し、実際の「次のノード」を示すリンク情報は各Beanに埋め込みま す。 – ここでの「next()」メソッドはこのListの仕組みの予約語になってしまうため、Bean側にnext()メソッドが 存在した場合は名前が重なってしまいます。よって、実際にはnext()のような重なりやすい名前ではなく、 もう少し重なりにくい長い名前にするか、「_next_()」のように、通常の命名規約から外れる名前にするこ とが望ましいです。 List first last Bean value next() Node next() Javaのインターフェース first last 0..1
  20. 20. ULS Copyright © 2011 UL Systems, Inc. 19 embedded pointersの他の例 (ポインタ差分による双方向リスト)  双方向リストを実現するにはnextとprevの2つのポインタを個々のノードに持つ必要があり、非 常に空間効率が悪くなってしまう。これを解決するための方法として、「ポインタ差分」という方法 がある。 List first last a:Node next prev 通常の 双方向リスト last first ポインタ差分を 使った 双方向リスト b:Node next prev c:Node next prev a:Node diff=d-b last first b:Node diff=a-c c:Node diff=b-d List first last d:Node diff=c-a 右方向への操作における計算方法 a=既知 d=既知 b=d-(d-b) c=a-(a-c) 左方向への操作における計算方法 a=既知 d=既知 c=(c-a)+a b=(b-d)+d d:Node next prev ※ ポインタ差分では、隣り合った2つのノードが分かっていれば、 その2つのノード情報を元に前後にリストをたどることが出来る。
  21. 21. ULS Copyright © 2011 UL Systems, Inc. 20 multiple representations  シチュエーション – 同一のAPIを持つ実装が、パフォーマンス特性などの差によって複数存在する場合があります。たとえば ListにおけるEmptyListとArrayListとLinkedListのように、場合によって選択可能なものがあります。  解決策 – (Javaの場合はListの例のように当然のように考えるものなので、パターンとして考える必要はないくらい のものです)  応用例 – Beanを1千万件ほど扱い、それらのBeanが「null状態」である場合が多い場合、「null専用Bean」を作る ことでデータ量を少なくすることが出来ます。 Bean a() b() StdBean a:int b:String a() b() NullBean a() b() フィールドを持たないことで、データを 減らすことが可能。 さらにimmutableオブジェクトであるこ とから、システム全体でsharing可能。 Java標準APIのEmptyListもこの考 え方に沿っている。
  22. 22. ULS Copyright © 2011 UL Systems, Inc. 21 memory allocations
  23. 23. ULS Copyright © 2011 UL Systems, Inc. 22 pooled allocation  シチュエーション – オブジェクトのnew(malloc)をランタイムに行うとパフォーマンスに影響があるが、アプリケーションの特 性としてオブジェクトの動的生成・破棄が必要な場合。(アクションゲームなど非常にシビアなレスポンスが 求められる場合や、組み込みシステムで遅延が許されない場合が典型例です)  解決策 – 固定数のオブジェクトプールを作り、システム初期化時にすべての必要なオブジェクトをメモリー上に展開 し、このオブジェクトプールからオブジェクトを借りる・返却するといった操作でオブジェクトを利用します。  応用例 – アクションゲームなどで、オブジェクトプールを作る場合。 (1画面に登場する最大キャラクター数までプールしておきます。) 自キャラ 敵キャラA 敵キャラB 敵キャラC ボスキャラA ボスキャラB ボスキャラC ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ オブジェクトプール システム機同時に全部ロード (グラフィックスなど含む) 借りる 返す
  24. 24. ULS Copyright © 2011 UL Systems, Inc. 23 おしまい

×