2009年1月3日土曜日

メモリの種類とか(いまさら)

昨日は久し振りに研究室の同期と会った。iPhone持ってたので見せてもらう。iPhoneスゲー。最近ブログ更新してねーよねって聞いたらむしろTwitter派らしい。Twitterなぁ、導入したらすごい気が散りそうでなんか手を出せないわ。気晴らしになんかいいのないか聞いたら、Travianがおすすめらしい。興味もったんで調べてみたがなかなかに複雑かつ難しそうじゃ…。
ネット上のゲームなりSNS的サービスなりってどうもあんまり知らん相手とコミュニケーションとることを求められることが多い気がして苦手だなぁ。人脈広げたりさらっと知り合い作るにはいいツールなんだろうけど、携帯メールと一緒で相手の表情読めない分、文章から心理状態を把握せにゃならん気がして無駄な深読みしまくって凄い疲れそう。

さてぼちぼち本業に復帰するか
たぶんこれから実装する計算方法はモーレツにメモリを喰うのでメモリの使われ方ってのを今更ながらに調べる。
メモリは大まかに分けて、コードセグメント、データセグメント、スタックセグメントの3種類のエリアに分けて使われるらしい。アドレス指定とかに使うレジスタもそれぞれに応じてCS,DS,SSが用意されてるとな?
コードセグメントはプログラムコードが機械語に翻訳された本体を収める部分。

データセグメントはデータ、.bssとヒープの3つに大別される。データ、.bssは静的な変数とグローバルな変数が納められる。データには初期化されてるのが、.bssには初期化なしのが振り分けられる。ヒープは動的に割りつけられる変数なり配列なりインスタンスなりに使われる。

スタックセグメントは手続き中の局所的な変数や配列、関数の引数の引き渡しなんかで使われる。

だいたいのところは上に書いたようになってるらしいが、具体的にどんな変数がどの領域に確保されるかというのは言語やコンパイラやその他もろもろによって変わってくる。
Fortranの場合だと
module中の大域的な変数、save属性付きの変数→.bss
allocatable属性な配列→ヒープ
普通にサイズが宣言されている自動配列→スタック
手続き中の変数(mainでも同様)→スタック
てのがデフォみたいだ。

フラットメモリモデルとかいう一般的な方法だとメモリの0x00000000番から順にコードセグメント、データセグメント、そしてメモリ0xffffffff番(32bitなOSでは1プロセスで使えるメモリの最大値らしい)から番号が小さいほうにむけてスタックセグメントが配置される。動的割り付けが行われるとヒープがアドレスの番号の大きいほうに伸びていき、関数の呼び出しなんかをしまくるとスタックが番号の小さいほうに伸びていく。で、動的割り付けでばかでかいのをやっちゃったり再帰手続きでばかでかい配列をつかったりするとヒープとスタックが衝突することがあるらしい。ふつー、スタックのサイズには制限がかかっていて、2~8Mになっている。Linuxだとlimitっていうコマンドで調べることができるらしい。スタックのサイズ制限はunlimit stacksizeで外せるのでそうするとこういう事態が起こってしまう可能性があるらしい。OSの側でそういうの監視してないんかよって気もするが、glibcはスタックがヒープ領域を破壊するのに無頓着らしい…。

そしてFortranの処理系ではヒープ領域へのアクセスがなんか怪しい。Fortranでの配列の動的割り付けはallocateで行われるんだが、たとえば allocate(A(2,2))と2×2の配列を割り付けたあとで、A(1,3)とか「あーそこ配列の定義外やろ…」ってとこに普通にアクセスできるし代入とかもできてしまう。コンパイルもされるし実行も普通にされるし(当然結果は変になるが)、gdbもスルーすることがある。動的な配列割り付けを使うとこのやりがちなバグを見つけるのが難しくなる。

アクセス速度自体もヒープは他に比べて遅いらしいので、数値計算なら配列は大きめに見積もってmoduleで静的に宣言しとくのが正解ってことか?

0 コメント: