64ビット環境ではポインタやスカラ型のバイト長がメモリ使用量を圧迫する。メモリ消費を最適化するには?Appleの開発者資料を整理、図解してまとめる。
NSInteger
Objective-cで頻出するNSIntegerはバイト長、4バイト(32ビット環境)から8バイト(64環境)になる。以下の構造体(struct)は6要素含む。6要素x4=24バイト圧迫するから、6要素x8=48バイト圧迫することになる。
struct date { NSInteger second; NSInteger minute; NSInteger hour; NSInteger day; NSInteger month; NSInteger year; };
uint32_tは32ビット長つまりは4バイト。当然、こちらがデータサイズは小さい。日付を細かく保持するより、unixtimeのようにある時からの時間を保存し計算や表示の時点で変換して年月日を生成する方法の提案。
struct date { uint32_t seconds; };
Paddingの問題
データ型(type)には決まった長さがある。メモリ上に配置する時にルールがある。揃え(alignment)を行う必要があり、空白(padding)が発生することがある。
ハードウェア視点で解説する。CPUの仕事の一つはメモリから読み込む事。CPUごとにルールがあり、1バイトずつ読む、2バイトずつ読む、4バイトずつ読むというのはCPUの設計に寄るのだ。4つずつ読むルールのCPUに3つずつデータをメモリに置いてもCPUは正しく理解できない。
※この理屈はハードウェアに依存し、ルールが異なるので注意が必要。
charは1バイト、int32_tは4バイト、int_64tは8バイトの長さを持つ。
次の構造体は複数のデータ型を持ち、4つの要素をもつ。
struct A { char a; int32_t b; char c; int64_t d; };
これをchar→int32_t→char→int64_tの順に構造体がメモリ空間に配置されれば、以下のように考えられる。
先ほどの通りハードウェアには決まりがある。それにあわせてコンパイラもメモリの内容がCPUに読み込まれ処理されるのが効率的(最小のCPUサイクルで)に行えるように最適化を行う。
- charは1の倍数ごとに配置
- int32_tは4の倍数ごとに配置
- int_64_tは8の倍数ごとに配置
これをもとに、以下のようにグレーの箇所も空白(Padding)として消費される。
このケースではトータルで24bytes消費する。
改良
struct A { int64_t d; int32_t b; char a; char c; };
- charは1の倍数ごとに配置
- int32_tは4の倍数ごとに配置
- int_64_tは8の倍数ごとに配置
のルールをもとにint64_t dから順にchar cまで配置されると次のようになる。結果14バイト。
ポインタの話題
struct node { node *previous; node *next; uint32_t value; };
このケースではLinkedListの形状。ADGの前後ストーリー関係を表現したり、ダイアログフローの制御に利用できるデータタイプである。この構造体を見ると次の事がわかる。
- 数値データを1つ含む
- ポインタを2つ含む
- uint32_tは4バイト消費する
- ポインタは構造体のアドレスを参照する
- 32ビット環境のポインタは4バイト消費(64bit環境では8バイト消費)する
32ビット環境では4バイトx3要素=12バイト消費、から64ビット環境では4バイトの数値要素+8バイトx2ポインタ要素=20バイト消費となる。ポインタ参照のメモリ消費量が2倍になるという点には注意が必要。
以下参照:
https://developer.apple.com/jp/devcenter/ios/library/documentation/CocoaTouch64BitGuide.pdf
コメント
Powered by Facebook Comments
One Response to “Objective-Cの64bit環境メモリ最適化”
Sorry, the comment form is closed at this time.
Objective-Cの64bit環境メモリ最適化 – http://t.co/jt6RCiV2jL