RAMを使いすぎています。これはどのように測定できますか?


19

私がプロジェクトで使用しているRAMの量を知りたいのですが、私が知る限り、実際にそれを実行する方法はありません(自分で計算して計算する以外に)。私はRAMが不足していると判断したかなり大きなプロジェクトの段階に到達しました。

セクションを追加すると、明確な理由もなく、コードのどこかですべての地獄が崩れるため、これを決定しました。私が#ifndef何か他のものを出したら、それは再び機能する。新しいコードにはプログラム的に問題はありません。

しばらくの間、使用可能なRAMの限界に近づいているのではないかと疑っていました。スタックを使いすぎているとは思いませんが(可能ですが)、実際に使用しているRAMの量を判断する最良の方法は何ですか?

調べて解決しようとすると、列挙型と構造体を取得するときに問題が発生します。どのくらいのメモリがかかりますか?

最初の編集:また、私は開始以来スケッチを非常に編集しました、これらは私が最初に得た実際の結果ではありませんが、私が今得ているものです。

  text     data     bss     dec     hex filename
 17554      844     449   18847    499f HA15_20140317w.cpp.elf
 16316      694     409   17419    440b HA15_20140317w.cpp.elf
 17346      790     426   18562    4882 HA15_20140317w.cpp.elf

最初の行(テキスト17554を含む)は多くの編集の後、機能していませんでしたが、2番目の行(テキスト16316を含む)は正常に機能しています。

編集:3行目には、すべての機能、シリアル読み取り、私の新しい関数などがあります。本質的にいくつかのグローバル変数を削除し、コードを複製しました。私はこれを言及しています(疑わしいとして)それはsaeごとのこのコードに関するものではなく、RAMの使用量に関するものでなければならないからです。これで元の質問「どのように最適に測定するか」に戻りますが、まだいくつかの答えをチェックしています。ありがとう。

上記の情報を実際にどのように解釈しますか?

これまでのところ、私の理解は次のとおりです。

`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS`  is variables occupying RAM

BSSは1024バイトよりかなり小さいので、なぜ2番目は機能しますが、最初は機能しないのですか?その場合DATA+BSS、両方が1024以上を占有します。

再編集:コードを含めるように質問を編集しましたが、実際には問題とは何の関係もなかったため、削除しました(コーディングプラクティス、変数宣言などの問題を除く)。本当に見たい場合は、編集内容を振り返ってコードを確認できます。手元の質問に戻りたいと思いました。これは、RAM使用量の測定方法に基づいています。


ここ数週間でコードのさまざまな新しいセクションを追加し、機能するまで最適化したが、今は半
ドーズ

Stringプログラムでtype を使用していますか?これは頻繁に動的なメモリの割り当てと解放を実行することが知られており、メモリを残さないようにヒープを断片化する場合があります。
jfpoilpret 14年

@jfpoilpret Stringオーバーヘッドのためにsから離れています。私は言ったchar配列、と幸せ働いている、私はほとんど常に固定サイズですべての私のchar配列を定義します(現時点では、私は別の再コンパイルのためのコンテンツの長さを変更するので、純粋でないONEバイト配列を持っています。
マディバード14年

ここにコードを投稿する(または大きすぎる場合はpastebinに投稿する)と、メモリで発生した問題を見つけることができます。
jfpoilpret 14年

@jfpoilpret本当にコードを投稿することはできません。それは巨大で、残念ながら非常に肥大化しており、16個のファイルに広がっています。これは、必要なものをはるかに超えて成長できるプロジェクトでした(いくつかのプロジェクトが統合されています)。私は今、それをバラバラに分解し始めています。これは問題を解決するのに役立つと確信しています。いくつかの部分がありますが、私は人々に見てもらう必要があります(または私をガイドします)が、後でそれらを投稿します。
マディバッド14年

回答:


15

AVRGCCで提供される機能を使用できます:スタック使用状況の監視

この関数は、スタックの使用状況をチェックすることを目的としていましたが、報告されるのは(実行中に)使用されたことのない実際のRAMです。これは、既知の値(0xC5)でRAMを「ペイント」(充填)し、その後、同じ初期値を持つバイト数をカウントしてRAM領域をチェックします。
レポートには、使用されていないRAM(最小空きRAM)が表示されるため、使用された最大RAM(合計RAM-報告されたRAM)を計算できます。

次の2つの機能があります。

  • StackPaintは初期化中に自動的に実行され、値0xC5でRAMを「ペイント」します(必要に応じて変更できます)。

  • StackCountは、使用されていないRAMをカウントするためにいつでも呼び出すことができます。

以下に使用例を示します。あまり機能しませんが、関数の使用方法を示すことを目的としています。

// -----------------------------------------------------------------------------
extern uint8_t _end;
extern uint8_t __stack;

void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));

void StackPaint(void)
{
#if 0
    uint8_t *p = &_end;

    while(p <= &__stack)
    {
        *p = 0xc5;
        p++;
    }
#else
    __asm volatile ("    ldi r30,lo8(_end)\n"
                    "    ldi r31,hi8(_end)\n"
                    "    ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
                    "    ldi r25,hi8(__stack)\n"
                    "    rjmp .cmp\n"
                    ".loop:\n"
                    "    st Z+,r24\n"
                    ".cmp:\n"
                    "    cpi r30,lo8(__stack)\n"
                    "    cpc r31,r25\n"
                    "    brlo .loop\n"
                    "    breq .loop"::);
#endif
} 


uint16_t StackCount(void)
{
    const uint8_t *p = &_end;
    uint16_t       c = 0;

    while(*p == 0xc5 && p <= &__stack)
    {
        p++;
        c++;
    }

    return c;
} 

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
Serial.println(StackCount(), DEC);  // calls StackCount() to report the unused RAM
delay(1000);
}

興味深いコード、ありがとう。私はそれを使用しましたが、600バイト以上の空きがあることを示唆していますが、それをより深い潜水艦に埋めると減少しますが、一掃しません。私の問題は他の場所にあるかもしれません。
マディバード14年

@Madivadこの600バイト以上は、StackCountを呼び出した時点までの使用可能なRAMの最小量を表していることに注意してください。StackCountを呼び出す前にコードの大部分とネストされた呼び出しが実行されていれば、呼び出しがどの程度深く行われても違いはありません。結果は正しくなります。そのため、たとえば、ボードをしばらく動作させたままにして(十分なコードカバレッジを得るか、理想的な動作を達成するまで)、報告されたRAMを取得するためにボタンを押します。十分であれば、それは問題の原因ではありません。
alexan_e 14年

1
@alexan_eに感謝します。ディスプレイ上にこれを報告するエリアを作成しました。今後数日間で進行するにつれて、特に失敗した場合にこの番号を興味を持って見ます。再びありがとう
マディバッド14年


そのおかげで、私は知っている、それが言及されている。私が知る限り、私はそれを使用していません(それを使用しているライブラリがあるかもしれないことは知っていますが、まだ完全にはチェックしていません)。
マディバッド14年

10

実行時のメモリ使用に関する主な問題は次のとおりです。

  • ダイナミックアロケーション用のヒープに使用可能なメモリがない(mallocまたはnew
  • 関数を呼び出すときにスタックに空きがありません

両方は実際にはAVR SRAM(Arduinoの2K)が両方に使用されるのと同じです(さらに、プログラム実行中にサイズが変わらない静的データに加えて)。

一般に、MCUでは動的メモリ割り当てはほとんど使用されず、通常は少数のライブラリのみが使用します(そのうちの1つはStringクラスであり、使用しないと述べていますが、それが良い点です)。

スタックとヒープは下の写真で見ることができます(Adafruit提供): ここに画像の説明を入力してください

したがって、最も予想される問題は、スタックオーバーフロー(つまり、スタックがヒープに向かって増加し、ヒープがオーバーフローした場合、およびヒープがまったく使用されなかった場合)がSRAMの静的データゾーンでオーバーフローすることです。次のいずれかのリスクが高い:

  • データの破損(つまり、スタックがヒープまたは静的データを上書きする)、理解できない動作を提供する
  • スタックの破損(つまり、ヒープまたは静的データがスタックの内容を上書きする)、通常はクラッシュにつながる

ヒープの最上部とスタックの最上部の間に残っているメモリの量を知るために(実際、下図のように同じ画像でヒープとスタックの両方を表す場合、最下部と呼ぶことがあります)、次の機能を使用できます。

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

上記のコード__brkvalでは、ヒープの最上部を指しますが、ヒープが0使用されていない場合です。この場合、&__heap_startどのポイントを使用して__heap_start、ヒープの最下部をマークする最初の変数を使用します。&vもちろんスタックの最上部(これはスタックにプッシュされる最後の変数です)であるため、上記の式はスタック(または使用する場合はヒープ)が使用できるメモリの量を返します。

この関数をコードのさまざまな場所で使用して、このサイズが劇的に削減されている場所を見つけてください。

もちろん、この関数が負の数を返すのを見た場合は遅すぎます。すでにスタックがオーバーフローしています!


1
モデレーターへ:この投稿をコミュニティwikiに投稿してすみません。投稿の途中で、入力中に何か問題を起こしたに違いありません。このアクションは意図的ではないため、ここに戻してください。ありがとう。
jfpoilpret 14年

この答えのおかげで、私は文字通りほんの1時間前にそのコードを見つけました(playground.arduino.cc/Code/AvailableMemory#.UycOrueSxfgの下部)。ディスプレイにデバッグするための非常に広い領域があるので、私はまだそれを含めていません(しかし、そうします)。ものを動的に割り当てることについて混乱していると思います。であるmallocnew私はそれを行うことができる唯一の方法は?もしそうなら、私は動的なものは何もありません。また、UNOに2KのSRAMがあることを知りました。私はそれが1Kだと思った。これらを考慮に入れて、私はRAMを使い果たしていません!他の場所を見る必要があります。
マディバッド14年

さらに、もありますcalloc。しかし、あなたは、あなたが知らなくても、動的割り当てを使用するサードパーティのLIBSを使用している可能性があります(必ずそれについてであることを、すべての依存関係のソースコードをチェックする必要があるだろう)
jfpoilpret

2
面白い。唯一の「問題」は、呼び出された時点で空きRAMを報告することです。したがって、適切な部分に配置しない限り、スタックオーバーランに気付かないことがあります。私が提供した機能は、その領域までの最小空きRAMを報告するので、その領域で利点があるようです.RAMアドレスが使用されると、それはもはや無料では報告されません(下側にはRAMが占有されている可能性があります「ペイント」値と一致し、空きとして報告されるバイト)。それとは別に、ユーザーが望むものに応じて、1つの方法が他の方法よりも適している可能性があります。
alexan_e 14年

いい視点ね!私はあなたの答えにこの特定の点に気づいていませんでした(そして実際にはバグのように見えた私にとって)、今私はフリーゾーンを前もって「描く」という点を見ています。たぶんあなたはあなたの答えでこの点をより明確にすることができますか?
jfpoilpret 14年

7

一時ディレクトリで生成された.elfファイルを見つける方法を見つけたら、以下のコマンドを実行project.elfして、生成された.elfファイルで置き換えられるSRAMの使用量をダンプできます。この出力の利点は、検査する能力であるどのようにあなたのSRAMが使用されています。すべての変数はグローバルである必要がありますか、本当にすべてが必要ですか?

avr-objdump -S -j .bss project.elf

project.elf:     file format elf32-avr


Disassembly of section .bss:

00800060 <__bss_start>:
        ...

00800070 <measurementReady>:
        ...

00800071 <cycles>:
        ...

00800073 <measurement>:
  800073:       00 00 00 00                                         ....

00800077 <measurementStart>:
  800077:       00 00 00 00                                         ....

0080007b <timerOverflows>:
  80007b:       00 00 00 00

以下のコメントで指摘されているIgnacio Vazquez-Abramsのように、これはスタックまたは動的メモリの使用を示していないことに注意してください。

さらに、a avr-objdump -S -j .data project.elfはチェックできますが、私のプログラムはどれもそれで何も出力しないので、それが有用かどうかはわかりません。「初期化された(ゼロ以外の)データ」をリストすることになっています


または、単に使用できますavr-size。ただし、動的割り当てやスタック使用量は表示されません。
イグナシオバスケス-エイブラムス14年

@ IgnacioVazquez-Abramsのダイナミクスについては、私のソリューションでも同じです。私の答えを編集しました。
ジッピー14年

さて、これはこれまでで最も興味深い答えです。私は実験してきたavr-objdumpavr-size、私はすぐに上記の私のポストを編集します。これをありがとう。
マディバッド14年

3

しばらくの間、使用可能なRAMの限界に近づいているのではないかと疑っていました。スタックを使いすぎているとは思いませんが(可能ですが)、実際に使用しているRAMの量を判断する最良の方法は何ですか?

手動推定とsizeofオペレーターを使用して組み合わせることをお勧めします。すべての宣言が静的である場合、これにより正確な画像が得られます。

ダイナミックアロケーションを使用している場合、メモリの割り当て解除を開始すると問題が発生する可能性があります。これは、ヒープ上のメモリの断片化によるものです。

調べて解決しようとすると、列挙型と構造体を取得するときに問題が発生します。どのくらいのメモリがかかりますか?

列挙型はと同じだけのスペースを取りますint。したがって、enum宣言に10個の要素のセットがある場合、それはになります10*sizeof(int)。また、列挙を使用するすべての変数は単にintです。

構造物についてはsizeof、調べるのに最も簡単です。構造は、そのメンバーの合計に等しい(最小)スペースを占有します。コンパイラが構造体の位置合わせを行う場合、それ以上の可能性がありますが、これはの場合には起こりそうにありませんavr-gcc


可能な限りすべてを静的に割り当てます。sizeofこの目的に使用することを考えたことがありません。現時点では、すでに(グローバルに)ほぼ400バイトを占めています。次に、列挙型を(手動で)計算して構造体(そのうちいくつかを使用しますsizeof)を計算し、レポートを返します。
マディバッド14年

sizeofこれがavrdude IIRCによって出力されるため、静的データのサイズを本当に知る必要があるかどうかはわかりません。
jfpoilpret 14年

@jfpoilpretそれはバージョンに依存していると思います。すべてのバージョンとプラットフォームがそれを提供するわけではありません。私のもの(Linux、複数のバージョン)は、1つのメモリ使用量を表示しませんが、Macバージョンは表示します。
アヒーシュ14年

私はそれがありませんが、私はそれがあるはずと思った、詳細な出力を検索しました
Madivad

@AsheeshR私はそれを認識していませんでしたが、私の場合はWindowsで問題なく動作します。
jfpoilpret 14年

1

Arduino Builderと呼ばれるプログラムがあり、プログラムが使用しているフラッシュ、SRAM、EEPROMの量をきちんと視覚化できます。

Arduino Builder

Arduinoビルダーは、CodeBlocks Arduino IDEソリューションの一部を構成しています。スタンドアロンプ​​ログラムとして、またはCodeBlocks Arduino IDEを介して使用できます。

残念ながら、Arduino Builderは少し古いですが、ほとんどのプログラムとUnoなどのほとんどのArduinoで動作するはずです。

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