通常、一部のデータをグローバル、静的、またはスタック上に置く必要があるかどうかを判断するのに問題はありません(ここでは動的割り当てがないため、ヒープを使用しません)。 this one のようないくつかのQ/Aも読んだことがありますが、システムメモリと比較して膨大な量のデータが含まれるため、私の質問はより具体的です。
私は改善しようとしている既存のコード(設計、考えられる問題、パフォーマンスなど)を作業しています。このコードは4KBのRAMのみの古い8ビットMCUで実行されます。このコードでは、ほぼ1KBの配列の使用に直面しています(はい、4KB RAMの1KBシステム)。この配列の各バイトが使用されますが、それは問題ではありません。問題は、この配列が宣言されているファイル内の静的配列であるため、そのライフサイクルはプログラムのものと同じである(つまり、無限と見なすことができる)ことです。
ただし、コードを読んだ後、この配列には無限のライフサイクルが必要ないことがわかりました。これは、完全に手続き的な方法で構築および処理されるため、使用される関数でのみ宣言できるはずです。これにより、スタック上にあるため、この1KBのRAMを節約できます。
さて、質問:これは良いアイデアでしょうか?設計の観点からは、無限/グローバルライフサイクルが必要ない場合、スタックに属します。しかし、ねえ、それは4KBのうちの1KBですRAM this this? 25%を割り当てることの欠点はありませんか?スタック)
誰かがこの種の状況についていくつかの経験を共有できますか、または誰かがこの配列をスタックに入れない正当な理由について考えますか?技術的な欠点と、デザインに関するコメントを探しています。
私が意識している唯一のことは、この関数に入るときに実際に1KBのスタックが空いていることを確認する必要があることです。多分それは私が気をつけなければならないことのすべてです、多分そうではありません。
私が意識している唯一のことは、この関数に入るときに実際に1KBのスタックが空いていることを確認する必要があることです。
はい、それは強い制約です。スタック上にこのような大きなスペースを確保するよりも、静的に確認する方がよいでしょう。コードが小さい場合、最近の [〜#〜] gcc [〜#〜] を使用してコードをコンパイルしている場合は、 this を参照してください。
ちなみに、一部の安価なマイクロプロセッサは、「大きな」コールフレームを「通常の」コールフレームよりも高価にする場合があります(たとえば、命令セットがスタックポインタからの1バイトオフセットを優先するため)。 YMMV。
また、Cでコーディングしていて、大規模な配列のスペースを他の目的で再利用できると思われる場合は、それを共用体メンバーにすることを検討してください(union
タイプのグローバル変数を使用)。はい、それはかなり醜いです。
または、アプリケーションに適したプリミティブヒープアロケーターをコーディングすることもできます(malloc
&free
....とは異なるAPIを使用できます)。
RAMで逆方向に増加し、変数の値を上書きするため、説明できない動作が発生するため、大規模なスタックでは慎重になる傾向があります。可能な限り低いスタックを知る必要があるため、さらに悪化します。ルーチンに入るときに割り当てるポインタアドレスとサイズを差し引きます。
これはすべて、ハードウェアのメモリ管理(スタックオーバーフローが発生したときにトラップまたは障害を生成する必要があります)またはコンパイラーの仕事です。
それ以外の場合は、RAMで必要なことを実行できます。
以前の回答で指摘したように、メモリに収まる場合は、配列を静的なままにしておくことをお勧めします。ほとんどの場合、たとえ常に使用されていない変数のメモリを「浪費」することを意味する場合でも、確定的なメモリフットプリントを持つことははるかに重要です。大きな配列をスタックに配置すると、簡単に吹き飛ばされてしまい、スタックオーバーフローは、見つけにくく、再現しにくい問題を引き起こす傾向があります(MMU toスタックを保護します)。
共用体を使用して他のデータとブロックを共有するという提案はIMOに有効ですが、間違った変数を見つけた場合、見つけにくい問題の原因となる可能性もあります。
メモリが不足していて、生存期間の短い変数を作成して共有する必要がある場合は、配列をスタックに移動する前に、独自の欠点がありますが、動的メモリ割り当てを追加することを検討します。この場合、配列は使用可能なメモリに比べてかなり大きく聞こえるので、答えにはならない可能性があります。
特に組み込みシステムで作業するときは、可能な限り多くの失敗をコンパイル時に発生させ、実行時に何も起こらないが失敗するようにします(ただし、これを達成できればいいのですが...)。
静的に割り当てられたプログラムの任意の状態で必要になる可能性のある大きな配列を作成すると、まさにそれが行われます-リンカは最終的に「これはRAMに適合しない」という警告を出しますが、スタックの割り当ては、デバッグが難しいスタックでプログラムをクラッシュさせるだけですオーバーフロー。
ある種のフラッシュストレージがある場合は、もう1つのオプションがあります。データをフラッシュに保存し、そこから読み取りと検索を行うことで、アクセス速度とRAMをトレードオフできます。一度に1つのレコードをRAMにロードするだけで済みます。レコードを更新できるようにする必要がある場合は、少し複雑になります。セグメント化された摩耗レベルのメカニズムが必要になります。私は過去にこれを行い、アクセスを高速化するためにインデックスを含めました。