組み込みシステムの単一アレイに膨大な量のスタックを割り当てることには欠点がありますか?


12

通常、一部のデータをグローバルにするか、静的にするか、スタック上に置くかを決定するのに問題はありません(ここでは動的な割り当てがないため、ヒープを使用しません)。このようないくつかのQ / Aも読んだことがありますが、システムメモリに比べて膨大な量のデータが含まれるため、私の質問はより具体的です。

改善しようとしている既存のコード(設計、考えられる問題、パフォーマンスなど)を扱っています。このコードは、RAMが4KBのみの古い8ビットMCUで実行されます。このコードでは、ほぼ1KBのアレイの使用に直面しています(はい、4KB RAMシステムでは1KB)。この配列の各バイトが使用されますが、それは問題ではありません。問題は、この配列は宣言されているファイル内の静的配列であるため、そのライフサイクルはプログラムの配列と同じである(つまり、無限と見なすことができる)ということです。

ただし、コードを読んだ後、この配列には無限のライフサイクルが必要ないことがわかりました。完全に手続き的な方法で構築および処理されるため、使用する関数でのみ宣言できるはずです。この方法ではスタック上にあるため、この1KBのRAMを節約します。

質問:これは良いアイデアでしょうか?設計の観点から、無限/グローバルライフサイクルを必要としない場合、スタックに属します。しかし、これは4KBのうち1KBですが、このようにRAMの25%を割り当てるという欠点はありませんか?(スタックの50%以上になる可能性があります)

誰かがこの種の状況の経験を共有できますか、または誰かがこの配列をスタックに入れない正当な理由について考えますか?技術的な欠点と設計に関するコメントを探しています。

私が意識している唯一のことは、この関数に入るときに実際に1KBのスタックが空いていることを確認する必要があるということです。多分それは私が世話をしなければならないことすべてであり、多分そうではない。


4
「したがって、この1KBのRAMを保存する」と書きました。何のために保存しますか?配列に必要な場合、その1KBが使用可能でなければならないので、静的割り当てを行わないのはなぜですか?配列に必要ないときに、メモリを別の用途に使用していますか?
kkrambo

@kkramboある時点で、静的なものでもスタック上のものでも、RAMに何も追加できないときにシステムがいっぱいになったと見なします。この配列を使用するときにだけスタックに配置すると、同時に使用されない限り、別の機能を配置できます。しかし、この質問は完全に合法です。現時点では、SWで何も変更しなければ、RAMはもう必要ありません;)
Tim

1
この配列に常に同じ内容が含まれているのか、それを使用する関数が呼び出されたときに変化するのかを明確にできますか?
Blrfl

@Blrfl関数が呼び出されるたびに変更されます。
ティム

回答:


8

私が意識している唯一のことは、この関数に入るときに実際に1KBのスタックが空いていることを確認する必要があるということです。

はい、それは強い制約です。スタック上にこのような大きなスペースを確保するよりも、静的に確認する方がよいでしょう。コードが小さい場合は、最近使用している場合は、GCCのコードをコンパイルするために、参照これを

ところで、一部の安価なマイクロプロセッサは、「通常の」コールフレームよりも「大きな」コールフレームを使用する場合があります(たとえば、命令セットがスタックポインタからの1バイトオフセットを優先するため)。YMMV。

また、Cでコーディングしていて、大きな配列のスペースを他の目的に再利用できると思う場合は、それをユニオンメンバー(union型のグローバル変数を持つ)にすることを検討してください。はい、それは非常にいです。

または、アプリケーションに適したプリミティブヒープアロケーターのコーディングを検討することもできます(APIにはmallocfree.... とは異なるものがあります)。


1
ありがとう、私は実際にこの種の答えを少し期待していました。つまり、スタックオーバーフローにならないように静的に割り当てられたままにしておきます。残念ながら、最近のGCCコンパイラーは持っておらず、コードは小さくありません。
ティム

(おそらく、ソースコードからGCCをコンパイルして)プラットフォーム用のGCCコンパイラーを入手できませんか?
バジルStarynkevitch

2
私はすでに持っています、それは本当に古いです。新しいものに取り組むことは私の範囲ではなく、私のスケジュールに合うものでもありません。しかし、確かに、最新のツールを使用するといくつかのことが簡単になります。
ティム

3
(おそらく最新のバイナリツールを提供するか、GCCの再コンパイルに時間をかけることで)上司に新しいGCCを取得するように依頼することは価値がありますgcc -flto -Os。 )あなたは、いくつかのメモリ....得るかもしれない
バジーレStarynkevitch

2
順調に進んでいますが、それはしばらく前には実現しません。私はすでに、より新しいツールチェーンを持つ他のシステムで-Osを使用していますが、この-fltoパラメーターを認識していませんでした。指摘してくれてありがとう。(数週間前にGCC 5.4.1でGCC -Osおよび-O1-3パラメーターを使用していくつかのテストを行ったが、RAMは常に同じであったことに注意してください。フラッシュとMCUの実行時間は異なりました。これは、このQ / Aで言及したものと同じMCUではありませんでした)
ティム

6

大規模なスタックでは、RAM内で後方に成長し、変数の値を上書きするため、説明できない動作につながるため、人々は慎重になる傾向があります。最悪の場合、スタックポインターアドレスの最小値を知り、ルーチンに入るときに割り当てるサイズを差し引く必要があるため、さらに悪化します。

これはすべて、ハードウェアメモリ管理(スタックオーバーフローが発生したときにトラップまたはフォールトを生成する)またはコンパイラーのジョブです。

それ以外の場合は、RAMを使用して必要な処理を実行できます。


4

以前の回答が指摘したように、メモリに収まる場合は配列を静的なままにすることもまずお勧めします。ほとんどの場合、常に使用されるわけではない変数のメモリを「浪費する」ことを意味する場合でも、確定的なメモリフットプリントを持つことがはるかに重要です。スタックに大きな配列を置くと、簡単に吹き飛ばされてしまい、スタックオーバーフローは、見つけにくく再現が難しい問題を引き起こす傾向があります(MMUを使用してスタックを保護できない場合)。

ユニオンを持つ他のデータとブロックを共有することの提案はIMOで有効ですが、間違った変数を見つけた場合、見つけにくい問題の原因になる可能性があります。

メモリが不足していて、それを共有するために必死に短命の変数を作成する必要がある場合、配列をスタックに移動する前に、動的メモリ割り当てを追加することを検討します。この場合、配列は利用可能なメモリと比較してかなり大きいように聞こえるので、答えではないかもしれません。


1

何らかのフラッシュストレージがある場合は、他に1つのオプションがあります。データをフラッシュに保存し、読み取りと検索を行うことで、RAMのアクセス速度と引き換えにできます。一度に1つのレコードをRAMにロードするだけです。レコードを更新できるようにする必要がある場合は、少し複雑になります。セグメント化された摩耗レベルのメカニズムが必要になります。過去にこれを実行し、アクセスを高速化するためのインデックスを含めました。


1
私も過去にこれをやったことがありますが、私の質問は、これをスタックに置いたときに直面する可能性のある問題に関するものであり、RAMが不足しているときのRAMの代替ではありません。とにかく答えてくれてありがとう
ティム

1

特に組み込みシステムで作業する場合、可能な限り多くの失敗をコンパイル時に発生させ、実行時には何も失敗しないようにします(ただし、これを達成できればいいのですが...)。

静的に割り当てられたプログラムの任意の状態で必要になる可能性のある大きな配列を作成すると、まさにそれが行われます-リンカーは最終的に「RAMに収まらない」と警告しますが、スタック割り当てはデバッグしにくいスタックでプログラムをクラッシュさせますオーバーフローします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.