トップページ⇒tech
82コメント28KB

スタックサイズの見積り

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさん2005/12/14(水) 13:25:43
適切なスタックサイズを見積もるのは、難しい問題です。
コンパイラのデフォルト値のまま、変えたことがない人も多いかと思います。

このスレッドでは、スタックサイズを適切に見積もる方法について議論します。
0002デフォルトの名無しさん2005/12/14(水) 13:30:39
__      __         __ 
   /    /__/       /  /
  /\   /   / || /  /  /
 /   \     /     /     / 
__|_
   /       /  \ ||
  / | ―― /    \  ――
 /  |     /     \
__      __
   /    /   /
  /    /__/   ――
 /           
      /\___/ヽ    
     /''''''   '''''':::::::\    
    . |(〇),   、(〇)、.:| 
    |   ,,ノ(、_, )ヽ、,, .::::|    
  .   |   `,rェェェ、 ' .:::::::| 
     \  |,r-r-|  .::::/     
  ,,.....イ.ヽヽ、`ニニ´ーノ゙-、.
  :   |  '; \_____ ノ.| ヽ i
      |  \/゙(__)\,|  i |
      >   ヽ. ハ  |   ||

地球のみんな、オラにスタックを分けてくれ――!!!
0003デフォルトの名無しさん2005/12/14(水) 13:36:13
int main(void) { return main(); }
0004デフォルトの名無しさん2005/12/14(水) 13:49:17
単発糞スレ立てるな

***終了***
0005デフォルトの名無しさん2005/12/14(水) 20:25:46
>>4
スレを1本使って議論するだけ奥の深い話だと思います。

質問スレで片付くような簡単な問題なら、
あなたが、ここで1レスで解決してみ。
0006デフォルトの名無しさん2005/12/14(水) 20:27:46
単発糞スレ立てるな

***終了***
0007デフォルトの名無しさん2005/12/14(水) 21:31:56
スタック領域って今でもデータセグメントと同じヒープに取られるの?
0008デフォルトの名無しさん2005/12/14(水) 21:39:04
>>7
OSによっては違うものがあった希ガス

>>2 ワロタ
0009デフォルトの名無しさん2005/12/14(水) 21:50:26
違うだろ。
スタックサイズを気にしなくて済むようにヒープをがんがん使うんだろ。
0010デフォルトの名無しさん2005/12/14(水) 21:52:50
単発糞スレ立てるな

***終了***
0011デフォルトの名無しさん2005/12/14(水) 22:23:52
昔なら、

プロセスの仮想メモリ空間はたっぷりあるから、100MBくらい割り当てておけ。

というのでもよかったけど、
最近は仮想メモリ空間はさほど広くないし、
マルチスレッドなので100MBずつ割り当てたら大変だね。

そこで64ビットですよ。
0012デフォルトの名無しさん2005/12/15(木) 05:35:42
自分がドライブ・フォルダを再帰的に検索しながら処理させる関数を作成して
組み込んでいた場合はスタックオーバーフローしないかと冷や冷やでしたが、
いざ検証チームに移ったら長いファイル名でのバグ出しが楽しくなりました。
0013デフォルトの名無しさん2005/12/15(木) 05:44:56
スタックサイズを気にしなければならないプログラムってどんなもの?
想像もつかんが!
0014デフォルトの名無しさん2005/12/15(木) 05:47:27
>>13
無知乙
0015デフォルトの名無しさん2005/12/15(木) 06:01:38
>>14
内部変数でこんなの定義しているヤツだろ
char buff[10000000];
0016デフォルトの名無しさん2005/12/15(木) 06:29:36
それは
static char buff[10000000];
で回避可能
0017デフォルトの名無しさん2005/12/15(木) 07:37:57
単発糞スレ立てるな

***終了***
0018デフォルトの名無しさん2005/12/15(木) 07:40:25
再帰関数の中に、スタック領域をどれだけ使用しているか概算するロジックを埋め込むことは可能?
0019デフォルトの名無しさん2005/12/15(木) 08:00:38
必死にごみネタ振ってるのがいるな
0020デフォルトの名無しさん2005/12/15(木) 08:17:36
>>18
可能
0021デフォルトの名無しさん2005/12/15(木) 15:50:12
>>16
副作用が大きいじゃん。

>>18
スタック上にあるオブジェクト(auto変数とか関数の引数とか)のポインタを比較すれば桶。
0022デフォルトの名無しさん2005/12/15(木) 15:59:42
C/C++でも末尾再帰にして__fastcall指定すれば、
スタックオーバーフローが起きる可能性は激減するの?
もちろんリターンアドレスの蓄積でオーバーフローするような深すぎる再帰は別として。
教えてエロい人。
0023デフォルトの名無しさん2005/12/15(木) 16:05:27
>>22
__fastcallとは何ですか?
0024デフォルトの名無しさん2005/12/15(木) 20:30:08
C言語ANSI化以降で使えるようになった構造体やC++クラスオブジェクトの
引数での実体渡しはスタックを喰うからなぁ。特に再帰では控えるべきだろうな。
0025デフォルトの名無しさん2005/12/15(木) 20:33:06
>>16
それだと再帰の一時エリアとして使えないじゃん。
malloc/freeやnew/deleteでないと。
0026デフォルトの名無しさん2005/12/15(木) 21:43:18
>>23
Pascal
0027デフォルトの名無しさん2005/12/15(木) 21:45:44
>>24
普通const参照にするだろ。(Cならconstポインタ)
0028232005/12/15(木) 21:59:46
>>26
えーとそれは、呼び出し規約の違いと言うことですか?
Pascalだと一般的なCとは逆順で積むのかな?
するってぇとfastcallはレジスタ渡し?
なんだか奇体なコンパイラだねぇ。
0029デフォルトの名無しさん2005/12/15(木) 22:06:36
>>22
末尾再帰の形にしたのなら人の手でループにできる。
0030デフォルトの名無しさん2005/12/15(木) 23:42:21
再帰を、C言語の関数コール&CPUのスタックで実装するのではなく、
自前のループとスタックなデータ構造で実装すれば、
ヒープは食うかもしれないが、CPUのスタックは食わないよね。
0031デフォルトの名無しさん2005/12/15(木) 23:44:37
プログラムが終了した時に、
スタックをどこまで使ったのか、
知りたいとは思う。

各関数の入り口あたりでスタックポインタをチェックして、
その最小値を保持するようにすればいいのかな。
それとも、メモリを特定のパターンで埋めておき、
あとで、どこまで書き換えられたのかチェックすればいいのかな。
0032デフォルトの名無しさん2005/12/16(金) 01:08:08
>>30
しかし折角のアルゴリズムとコード記述の簡潔さと美麗さが失われる罠
0033デフォルトの名無しさん2005/12/16(金) 02:07:12
>31
DOSの頃は特定パターンで埋めとく方法だった。
今はどうなってんのか知らん。

「あとで」と言うのがどの時点かでだいぶ変わって来る。
0034デフォルトの名無しさん2005/12/16(金) 02:52:57
>>31
今のWindowsなんかだとOSのメモリ管理機構を通すし、スタックもプロセスごと
に割り当てられるから、プログラム終了後に確認なんてできないだろう。
0035デフォルトの名無しさん2005/12/16(金) 10:52:09
>>34
その関数をatexit()に登録しておくとか、そもそもスタートアップルーチンを差し替えるとか。
いずれにしても、デバッグ用のスタートアップルーチンを持っている開発環境なら既にやっていることだ。
0036デフォルトの名無しさん2005/12/16(金) 11:26:42
大体一番深くなるであろう場所は作者自身が分かってるから
mainで最初にスタックのアドレス覚えておいて
深くなる関数内で同様にauto変数のアドレス調べればいい
0037デフォルトの名無しさん2005/12/16(金) 14:05:21
Windowsの場合、スレッド単位でスタックサイズを変えることができるので、('A`)マンドクセか。
0038デフォルトの名無しさん2006/05/05(金) 15:39:47
pushとpopを数えて一番大きい時の数を調べればいいのでは?

自己呼び出しな関数が一つでもあれば破綻する方法だが。
0039デフォルトの名無しさん2006/05/06(土) 06:35:04
>>37
遅レスだが、スレッド単位でスタックサイズを変えられない OS ってあるの?
0040デフォルトの名無しさん2006/05/10(水) 18:19:55
そもそも、スタックサイズが有限なのはダサい。
64ビットにして、実質上無限になるように、それぞれの領域を離しておくべきだ。

1プロセスで1000スレッドとかのプロセスはどーするんだって? 紫蘭がナ。
0041デフォルトの名無しさん2006/05/10(水) 20:15:17
x86ならプロテクトモードではスタックオーバーフロー時に自動で大きさを拡張できるセグメントが作れる。
勿論セグメントを使わないWindowsやその他大抵のOSは使っていない。
0042デフォルトの名無しさん2006/05/10(水) 20:46:16
>>41
アドレス空間が足りなくなるから、あらかじめスタックサイズを指定して、アドレス空間を節約する必要があるのですよ。
0043デフォルトの名無しさん2006/05/12(金) 16:04:04
>>42
0:32 モデルではなくちゃんと ss 使えばメモリ空間的にも大丈夫、って話では?
0044デフォルトの名無しさん2006/05/12(金) 23:10:36
>>43
AdvancedWindows嫁
0045デフォルトの名無しさん2006/05/13(土) 12:38:28
>>43
もし、そうすると、

ポインタを単なるスカラー値ではなく、
アドレス値に加えてセグメント値も持つベクトル値にする必要があるよ。
16ビットの頃の、farポインタって奴みたいにさ。
0046デフォルトの名無しさん2006/05/13(土) 17:59:46
悪夢のfarポインタ
middleモデル、largeモデルなんて忘れたぜ
0047デフォルトの名無しさん2006/05/13(土) 19:36:33
>>45
いやそれは必要ないし、アプリ側では物理アドレスがどこかなんて判らない。
ただ、ローカル変数なんかのアクセスにいちいち SS: が必要になるんで、
当時は嫌だったんだろうな。

今となってはそうすべきだったと思えるが
0048デフォルトの名無しさん2006/05/13(土) 23:52:54
>>46
たしかに忘れてるようだな。
middleモデルじゃなくてmediumモデルじゃなかったか?
0049デフォルトの名無しさん2006/05/14(日) 00:52:34
この板の住人のみんなはceleronとpentiumとamdとその他のどれ使ってる?
0050デフォルトの名無しさん2006/05/14(日) 07:28:48
それがスタックサイズとどのような関係が?
0051デフォルトの名無しさん2006/05/14(日) 15:09:54
>>47
> いやそれは必要ないし、アプリ側では物理アドレスがどこかなんて判らない。
物理アドレスの話なんて誰もしていないぞ。

> ただ、ローカル変数なんかのアクセスにいちいち SS: が必要になるんで、
> 当時は嫌だったんだろうな。

まだわかっていないようだな。

スタック上のオブジェクトへのポインタ
ヒープ上のオブジェクトへのポインタ
この2つを区別しなくてはいけないので、
32ビットの仮想アドレス値に加えて、
どのセグメントレジスタを使うのかを示すものが必要になる。

どちらか片方しか受け取れない関数なんてのは使いにくいので、
両方に対応することになるわけで、オーバーヘッドが馬鹿馬鹿しいよ。

ポインタの指す先にアクセスする度に、DSとSSどちらを使うかで条件分岐するなんてさ。
0052デフォルトの名無しさん2006/05/14(日) 16:45:26
>>51
そうだったな。
BPをSPの代わりにローカルスタックとして使うしかないか
0053デフォルトの名無しさん2006/05/14(日) 22:04:37
>>52
ローカルスタックって何さ。
関数ないローカル変数ならスタック上に確保した後BPでアクセスするのが常套手段だけどそのこと言ってるわけじゃないよね?

つーか、世の中x86だけじゃないんだから少しは他も勉強してみたら?
0054デフォルトの名無しさん2006/05/15(月) 05:54:31
>>53
ごめん。
PCとレジスタの保存は SS:SP に ローカル変数は BPをスタックとして使って保存させるという意味

0055デフォルトの名無しさん2006/05/15(月) 18:36:21
>>52
何を言いたいのか、わからない。

スタックとヒープを別々のアドレス空間に取ることで発生する問題を、
> BPをSPの代わりにローカルスタックとして使う
ことで、どのように解決するのか、くわしく説明してよ。
0056デフォルトの名無しさん2006/05/15(月) 19:50:47
>>55
具体的に言うと、
関数の頭で、BPをPUSH した後
必要サイズだけ BP を マイナスする そして、BP相対でローカル変数をアクセスする
関数の終わりで BP をPOPして RET する。


BPの指す領域はデータ領域なので、この領域を他の関数で参照する時には、当然
問題は発生しない。


関数の帰り番地、BPの保存とか
関数の途中で他のレジスタの保存が必要になれば
PUSH/POPする。 これはスタック領域だけど、この領域は参照されるべき領域じゃない
0057デフォルトの名無しさん2006/05/15(月) 20:03:34
他の関数を呼び出す時の引数は、BPより前に書いてからCALLする
これはハードウエアスタックではないのでBPより前に書いても壊れる心配はない
0058デフォルトの名無しさん2006/05/15(月) 20:17:16
>>56
スタック上にデータを置かずにBPでデータ専用スタックを使うって話ですよね。
そのデータ専用スタックについて、
>>40-45の問題が発生するだけのような・・・
0059デフォルトの名無しさん2006/05/15(月) 20:33:59
>56
>BPの指す領域はデータ領域なので、
BPのデフォルトのアクセス先はSS
0060デフォルトの名無しさん2006/05/15(月) 20:36:44
>>59
そうなんだよね。 だからいちいち DS: が必要だ
0061デフォルトの名無しさん2006/05/15(月) 22:43:53
>>56
それは問題の解決になっていないよ。

スタック上の、ポインタで参照されるオブジェクトを、
ヒープなどと同じアドレス空間に設けた別のスタックに移す
ということでは、>>58さんの言うように、別のスタックの空間の割り当ての問題がでてくる。

それに、スタックからポインタで参照されるオブジェクトを追い出してしまったら、
必要なスタックサイズがかなり小さくなるし、
誰もポインタで参照しなければ、スタックの中身は移動させることが可能になるよ。
0062デフォルトの名無しさん2006/05/15(月) 23:55:45
昔の MAC みたいに、ポインタなんか使わずに全部ハンドルにするんだ。

俺は、嫌だが。
0063デフォルトの名無しさん2006/05/16(火) 13:08:24
俺言語作るなら、フレーム自体はヒープに確保してスタックには
フレームポインタや戻り番地とかしか置かないってのも良いような・・
ヒープはコンパクションしないっていう方針ならフレームポインタは
ロードし直さなくて良いし。

大き目のローカル変数は構造体(等)にしてスマートポインタで確保、
というのを自動的にやってくれるイメージ。
0064デフォルトの名無しさん2006/05/16(火) 14:57:55
そうなると、再帰しなければ、スタックサイズは高々64KBもあれば十分なんだよね。
スタックを詰め込んでいいわけで、メモリ空間を無駄に予約する必要がなくなる。
0065デフォルトの名無しさん2006/05/17(水) 22:44:18
そんなことしたら、マルチスレッド環境ならヒープが穴だらけになってすぐ
破綻することぐらい想像できないのか...。
0066デフォルトの名無しさん2006/05/17(水) 22:56:33
もうオブジェクト毎にスタック領域を オブジェクトインスタンスに取ってしまおう

で、副作用として実行保留が簡単に実現出来るから
クライアントとして作ったオブジェクトがサーバに簡単に変身出来ると
0067デフォルトの名無しさん2006/05/18(木) 16:42:22
>>65
想像できないね。

ヒープのオーバーヘッドでリソースを無駄に食うことはあっても、破綻はしないでしょ。
ヒープが穴だらけになってヤバくなるのはオブジェクトのサイズがとても大きい場合。
現状でスタックに積むような小さなオブジェクトなら、あいた穴がすぐに再利用されるから。
0068デフォルトの名無しさん2006/05/18(木) 22:22:14
> 大き目のローカル変数は構造体(等)にしてスマートポインタで確保、

と言いながら、

> ヒープが穴だらけになってヤバくなるのはオブジェクトのサイズがとて
> も大きい場合。

って、あふぉですか?
0069デフォルトの名無しさん2006/05/19(金) 10:59:04
>>68
まあでももともとスタックにとるようなものだから、寿命は短いのではあるまいか。
とすればメモリ/アドレス空間に余裕があればそのうち解消する・・・

というかヒープ/メモリ空間の穴空きに関して言えば、
現状のスレッド毎にアドレス範囲を設定してFIFOで使うというのよりは
マシじゃないだろうか(速度的には不利だけど)。
0070デフォルトの名無しさん2006/05/19(金) 14:50:23
>>68
「大き目」
「とても大きい」
その違いがわからないなんて。

ヒープが穴だらけになって、
メモリは余っているのに、連続したアドレス空間がない
という破綻した状態になるのは、
スタックには確保できないような巨大なオブジェクトの話ですよ。
100KBとか1MBを越えるような、そんなサイズの。
0071デフォルトの名無しさん2006/05/19(金) 16:14:05
というか、スレッド毎に、とりあえず別の領域を割り当てていけばいいじゃない
0072デフォルトの名無しさん2006/05/19(金) 20:18:27
>>70
> メモリは余っているのに、連続したアドレス空間がない
> という破綻した状態になるのは、
> スタックには確保できないような巨大なオブジェクトの話ですよ。

違うよ。
どんな大きな領域でも同じようなサイズなら破綻はしにくい。
「大き目」と「とても大きい」サイズが混在するから穴があいて破綻するんだよ。
まさか、そんなことも知らないの?

>>71
それが割り当てられるなら、普通にスタックとして割り当てておけばいいだけ。
そしたら、>>64 もこんなあふぉな考えは浮かばなかっただろうにね。(w
0073デフォルトの名無しさん2006/05/19(金) 20:47:31
いいや、72が間違っている。

ヒープに取る方式なら、最初はスレッド毎に確保した領域を使い、
一杯になれば別の領域を使うという手が使える

つまり、スタックサイズの見積もりがたたないという問題の解決になる
0074デフォルトの名無しさん2006/05/19(金) 22:11:19
>>72
よくわからんけどさ、小さめのモノはスタックに置こう、
スタックに置くのはヤバげな大きいのはヒープに置こう、
ってのは人間が普段やってることなわけで、
それを否定されると C/C++ ではマルチスレッドアプリ書けなくなるし・・・
0075デフォルトの名無しさん2006/05/20(土) 04:10:46
>>72
それは、いったい、いつの時代のヒープの話ですか?

普通のCのプログラムで、
色んなサイズのオブジェクトをヒープに置いて、
破綻したという話なんて聞いたことがないよ。
0076デフォルトの名無しさん2006/05/20(土) 04:37:08
まぁ君の場合はOSが勝手にメモリコンパクションをやってくれているからな。
0077デフォルトの名無しさん2006/05/20(土) 06:56:36
このスレは、特定の環境を前提にしてるのか?
0078デフォルトの名無しさん2006/05/20(土) 09:13:04
>>73
それは、ヒープとる時にいろいろな処理ができるというだけのこと。
スタックとる時に余計な処理を入れていいなら、スタックでもいっぱい
になったら別の領域を取ることは可能。
性能が犠牲になるからやってないだけのこと。
0079デフォルトの名無しさん2006/05/20(土) 09:32:43
>スタックでもいっぱい になったら別の領域を取ることは可能。

巨大なローカル変数を取る場合は先頭で自己チェックを入れて
オーバーしそうなら別の領域にSPを取って
新しいSPに古いSPを保存、
自分の帰り番地になる場所に短いSP復元コードへのアドレスを書き込んでから実行
RET命令で、その復元コードに飛んで領域を消去してから元のSPに返るという感じかな

でもそれなら、最初から巨大なローカル変数はヒープに取ればいい事だな
0080デフォルトの名無しさん2006/05/22(月) 13:17:46
まとめ:

大き目のローカル変数をヒープに取るとマルチスレッド環境で破綻するので、
コンパクション無しのメモリ管理ではマルチスレッドアプリは書けない。
0081デフォルトの名無しさん2006/05/24(水) 15:59:53
>>80
話の流れを無視するなよ。

> 大き目のローカル変数をヒープに取るとマルチスレッド環境で破綻する
>>77

> コンパクション無しのメモリ管理ではマルチスレッドアプリは書けない。
>>77

>>76がキチガイなだけだから惑わされるな。

Windowsはヒープのコンパクションなんてやってくれないが、
マルチスレッドのプログラムで破綻するなんてことは、
まず普通はないから。
0082812006/05/24(水) 16:00:53
Windowsは、例として挙げただけだからな。
Windowsを前提とした話ではないからな。
■ このスレッドは過去ログ倉庫に格納されています