Mar 222014
 

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バイトの長さを持つ。スクリーンショット 2014-03-22 16.30.20

次の構造体は複数のデータ型を持ち、4つの要素をもつ。

struct A {
    char a;
    int32_t b;
    char c;
    int64_t d;
};

これをchar→int32_t→char→int64_tの順に構造体がメモリ空間に配置されれば、以下のように考えられる。 スクリーンショット 2014-03-22 16.30.37

先ほどの通りハードウェアには決まりがある。それにあわせてコンパイラもメモリの内容がCPUに読み込まれ処理されるのが効率的(最小のCPUサイクルで)に行えるように最適化を行う。

スクリーンショット 2014-03-22 16.30.20

  • charは1の倍数ごとに配置
  • int32_tは4の倍数ごとに配置
  • int_64_tは8の倍数ごとに配置

これをもとに、以下のようにグレーの箇所も空白(Padding)として消費される。

スクリーンショット 2014-03-22 16.30.50

このケースではトータルで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バイト。

スクリーンショット 2014-03-22 16.58.25

ポインタの話題

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

 Posted by at 17:24

Objective-Cの整数表現

 プログラミング  Comments Off on Objective-Cの整数表現
Mar 222014
 

Objective-Cでは、整数表現にintやuintやNSUInteger等様々ある。NSString文字変換したり、計算したり、カウントしたりNSArray配列に格納したり様々使い分ける。Objective-Cでのプログラミングには様々な整数表現の違いが出てくる。

Integerとは?
英語の整数の意味。プログラミングでは整数型。 整数とは何か?wikipediaによると、

0 とそれに 1 ずつ加えていって得られる自然数 (1, 2, 3, …) および 1 ずつ引いていって得られる数 (−1, −2, −3, …) の総称である。

http://ja.wikipedia.org/wiki/整数

UIntegerとは符号無し(Unsigned)の整数(Integer)。つまり、Objective-CでのNSUIntegerは正の整数を扱う数である。

符号とは、コンピュータ内部での数値の正負(ゼロより大きいか小さいか)の表現方法…

http://ja.wikipedia.org/wiki/符号付数値表現

NSIntegerかintか?NSUIntegerかuintか?

深い事を抜きにすれば、intはNSIntegerとイコール、uintはNSUIntegerとイコールと使える。
特別な要求が無い限りはNSUInteger/NSIntegerを使うのがベター。具体的な違いは以下のリンク。

http://stackoverflow.com/questions/4445173/when-to-use-nsinteger-vs-int

NSNumberとは?
数を表すオブジェクト型。NSArrayやNSMutableArrayやCoreDataにはプリミティブ型(NSUInteger)を格納できない。 この場合、オブジェクト型(NSNumber)に変換をする。NSArrayでは、格納できるデータはオブジェクト型になる一方、添字となる番号の型はプリミティブ型(NSUInteger)。

定義まとめ
プリミティブ型では*記号無し…

NSInteger num=1;
int num=1;
uint num=1;

オブジェクト型では*記号あり…

NSNumber *num=1;

変換まとめ
プリミティブ型(NSUInteger)→オブジェクト型(NSNumber)

[NSNumber numberWithUnsignedInt:1];

オブジェクト型(NSNumber)→プリミティブ型(NSUInteger)

[NSNumber unsignedIntegerValue]

プリミティブ型(NSUInteger)→オブジェクト型(NSString)

NSString *str = [NSString stringWithFormat:@"%d", 1];

64bit環境への移行

(2014/03追記)
以下、64bit環境に移行する場合のコード記述の注意点。

  • 64ビット long 整数を32ビット整数に代入しない
  • 64ビットポインタを32ビット整数に代入しない
  • 算術演算の際に起こるポインタや long 整数の切り捨て(truncation)(その他、整数型の違いによる算術演算上の問題)を避ける
  •   データ型のバイト長の違いによって起こる、揃え境界(alignment)の問題を解消する。
  • 32ビットランタイム、64ビットランタイムで共通に用いる構造体は、メモリ上の配置が同じであるようにする。
  • アセンブリ言語で記述したコードは、64ビット版の新しい命令コード(opcodes)やランタイムに合わせて書き直す
  •   可変引数の関数を固定引数の関数に(あるいはその逆に)キャストすることは避ける

(下記から一部引用)

https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40013501

https://developer.apple.com/jp/devcenter/ios/library/documentation/CocoaTouch64BitGuide.pdf

 Posted by at 12:16