2011-08-11 5 views
2

スタックフレームがより頻繁に割り当て解除されるので、ある時間にメモリ内の変数が少なくなるので、1つの大きな関数とは対照的に、5つの関数に分けられます。それはコンパイラと最適化に依存していますか?もしそうなら、どんなコンパイラが速いのですか?変数がより頻繁に解放されるので、より小さい関数を一般的により効率的にメモリに効率的にしていますか?

答えには多くのローカル変数があり、スタックフレームは中央のメインから来てお互いの上には作成されません。

私は、関数を小さな関数に分割する他の利点を知っています。メモリ使用量に関してのみこの質問に答えてください。

+0

メモリを最小限に抑えるようにコードを構成しないでください。あなたに最も合って理解しやすい方法でそれを行います。マイクロ最適化は時間の無駄です。 –

+1

あなたのコンピュータは1970年に建てられましたか?それは[PDP11](http://en.wikipedia.org/wiki/PDP-11_architecture)のような限られたアーキテクチャですか? –

+0

いいえ、読みやすくする方が効率的です。プログラマはコンピュータメモリよりもはるかに高価です。 –

答えて

2

それはあなたのプログラムのスタック使用量の「ハイウォーターマーク」を減らし、そしてかもしれないプログラムの全体的なメモリ要件を減らす可能性があるようであれば。

はい、最適化によって異なります。オプティマイザが関数呼び出しをインライン化すると、インライン化されたすべての関数のすべての変数が1つの大きなスタックフレームにラップされることがわかります。使用価値のあるコンパイラは[*]をインライン化することができるため、コンパイラに依存しないという事実があります。まさにそれが起こったときに違います。

ローカル変数が小さい場合は、起動時に自動的に割り当てられたスタックより多くのスタックをプログラムで使用することは非常にまれです。あなたが最初に与えられたものを過ぎていない限り、どれくらい使っても全体的なメモリ要件には何の違いもありません。

スタックに大きな構造体(複数キロバイト)を置いている場合、またはキロバイトが大量のメモリを搭載しているマシンでは、全体的なメモリ使用量に影響を与える可能性があります。だから、 "たくさんのローカル変数"によって何十ダースを意味するのであれば、intとポインタはいいえ、あなたは何も重要な違いはありません。 「多くのローカル変数」によって数十kのバッファを意味する場合、または関数が非常に深く再帰して数十億のレベルの何百ものレベルを持つ場合は、それは可能な限り少なくなる可能性がありますOSと設定で

一般的なRAMによってスタックとヒープが相互に向かって成長し、中央の空きメモリがどちらか一方でも同等に使用できるモデルは廃止されています。ごくわずかな、非常に限られたシステムを除いて、メモリモデルはそれ以上は設計されていません。現代のOSでは、いわゆる「仮想メモリ」があり、スタック・スペースは一度に1ページずつプログラムに割り当てられます。それらのほとんどは、通常は非常に大きな設定された制限まで、使用されているスタックのページを自動的に割り当てます。いくつかのものは自動的にスタックを拡張しません(Symbianは最後に使っていましたが、これは数年前のことでしたが、Symbianは "現代的" OSではないと言えます)。組み込みOSを使用している場合は、スタックに関するマニュアルの内容を確認してください。

いずれにしても、合計メモリ使用量に影響を与えるのは、一度に必要なスタックページ数だけです。システムがスタックを自動的に拡張する場合、使用量に気づかないことさえあります。そうでない場合は、プログラムに最高水準点のスタックが十分に与えられていることを確認する必要があります。要するに、これは理論的に違いを生むものの1つですが、実際にはその差はほとんど常に重要ではありません。

[*]基本的には最適化されていないアセンブラであるCコンパイラを使用して、PICなどでC言語でプログラミングしている人は、プログラムが大量のスタックを使用する場合にのみ問題になります。私は彼らのコンパイラを "使う価値がない"と呼んだことを怒らせてしまいます。そのようなデバイス上のスタックは、回答が異なっている「典型的な」システムとは非常に異なっています。

1

私はたいていの場合、(プログラム全体の)スタックに割り当てられたメモリの領域は一定のままだと思います。使用量の使用は、呼び出しスタックの深さに基づいて変更されますが、使用される変数の数が少なくなるとその量は少なくなります(ただし、関数呼び出しは戻りアドレスとスタックポインタも押します)。

また、関数の呼び出し方法によっても異なります。たとえば、2つの関数が直列に呼び出され、最初のスタックが2番目の関数の呼び出しの前にポップされた場合、スタックの使用量は少なくなります。しかし、最初の関数が2番目の関数を呼び出した場合、 1つの大きな関数(関数呼び出しのオーバーヘッド)を使用していた場所に戻ります。

0

はい、ジェット面に塗料を細かく塗ると空気力学的特性が向上します。確かにそれは悪い類推ですが、物事を明確かつ電信的にしたり、より多くの機能を使用しようとする疑問が残っている場合は、電信で行ってください。初心者がサブルーチンや関数をあまりにも多く与える傾向があるため、ほとんどの場合、これらは相互排他的ではありません。

私は、あなたが本当に仕事を分けているなら(f、g、h)、使用可能なメモリが少し増えているのを見ていますが、もしこれらが相互に依存していればあなたはそうしないと思います。

@Joel Burget氏によると、メモリ管理は実際にコードを構成する上での考慮事項ではありません。

私のテイク。

0

スタックにメモリ割り当てはありません。スタックポインタを次の値に向かって移動するだけです。スタックサイズ自体はあらかじめ定義されています。したがって、メモリ使用量に違いはありません(スタックオーバーフローが発生する状況は別です)。小さなものに巨大な機能を分割

+0

スタックポインタを上に移動すると、メモリがいっぱいになります。スタックとヒープはお互いに向かって成長します。より多くのスタックスペースやヒープスペースを使用するということは、基本的には同じことです。空きメモリが少なくなります。 – theRealWorld

+0

ページは物理的には使用されませんが、予約され、システムのコミット制限から差し引かれます。私の知る限り – Oleg

0

は、それらの間潜在的に最適化されたメモリ使用量があり、その利点を持っています。

あなたにこの機能があります。

void huge_func(int input) { 
    char a[1024]; 
    char b[1024]; 
    // do something with input and a 
    // do something with input and b 
} 

あなたは2つに分割します。 huge_funcを呼び出す

void func_a(int input) { 
    char a[1024]; 
    // do something with input and a 
} 

void func_b(int input) { 
    char b[1024]; 
    // do something with input and b 
} 

は、メモリの少なくとも2048のバイトを取り、その後、func_bfunc_aを呼び出して約半分より少ないメモリと同じ結果を実現します。ただし、func_a内にfunc_bと呼ぶ場合、使用されるメモリ量はhuge_funcとほぼ同じです。本質的に、@ sje397が書いたもの。

これは間違っているかもしれませんが、スタックメモリの使用量を減らすのに役立つコンパイラの最適化はないと思います。私は、スタックメモリのレイアウトは、宣言されたすべての変数に対して、使用されているかどうかにかかわらず、十分なメモリが予約されていることを保証しなければならないと考えて

関連する問題