Alex AllainによるJumping into C ++、16章:Recursion、p.230 を読んでいたのとまったく同じ質問があったので、いくつかのテストを実行しました。
TLDR;
私のArduino Nano(ATmega328 mcu)は、スタックオーバーフローが発生してクラッシュする前に、211の再帰関数呼び出し(以下に示すコードの場合)を実行できます。
最初に、この主張に対処しましょう。
特定のアルゴリズムを実装するための唯一の迅速なオプションが再帰である場合があります。
[更新:ああ、「クイック」という単語を読みました。その場合、ある程度の妥当性があります。それでも、私は次のことを言う価値があると思います。]
いいえ、それは本当のことだとは思いません。すべてのアルゴリズムには、例外なく再帰的ソリューションと非再帰的ソリューションの両方があると確信しています。それは時々それがかなり簡単だということです再帰アルゴリズムを使用します。そうは言っても、再帰はマイクロコントローラーで使用するために非常に嫌われており、おそらく安全上重要なコードでは許可されないでしょう。それにもかかわらず、マイクロコントローラでそれを行うことはもちろん可能です。特定の再帰関数にどのように「深く」入ることができるかを知るには、テストするだけです!実際のテストケースの実際のアプリケーションで実行し、無限に再帰するように基本条件を削除します。カウンターを印刷して、どのように「深く」進むことができるかを確認して、再帰アルゴリズムが実際に使用するにはRAMの限界に近づきすぎていないかどうかを確認してください。以下は、Arduinoでスタックオーバーフローを強制するための例です。
さて、いくつかのメモ:
再帰呼び出し、つまり「スタックフレーム」をいくつ取得できるかは、次のような多くの要因によって決まります。
- RAMのサイズ
- スタック上に既にあるもの、またはヒープに占有されているもの(つまり、空きRAMが重要です;
free_RAM = total_RAM - stack_used - heap_used
または、言うかもしれませんfree_RAM = stack_size_allocated - stack_size_used
)
- 新しい再帰関数呼び出しごとにスタックに配置される新しい「スタックフレーム」のサイズ。これは、呼び出される関数、その変数、メモリ要件などに依存します。
私の結果:
- 20171106-2054hrs-Toshiba Satellite w / 16 GB RAM; クアッドコア、Windows 8.1:クラッシュ前に出力される最終値:43166
- クラッシュするのに数秒かかりました。5〜10ですか?
- 20180306-1913hrs 64 GB RAMを搭載したDellハイエンドラップトップ; 8コア、Linux Ubuntu 14.04 LTS:クラッシュ前に出力される最終値:261752
- フレーズが続きます
Segmentation fault (core dumped)
- クラッシュするのに約4〜5秒ほどかかりました
- 20180306-1930hrs Arduino Nano:TBD ---〜250000でまだカウント中です--- Arduinoの最適化設定により、再帰を最適化する必要がありました... ??? はい、そうです。
#pragma GCC optimize ("-O0")
ファイルの先頭に追加してやり直します:
- 20180307-0910hrs Arduino Nano:32 kBフラッシュ、2 kB SRAM、16 MHzプロセッサー:クラッシュ前に印刷された最終値:211
Here are the final print results:
209
210
211 ⸮ 9⸮ 3⸮
- 115200のシリアルボーレートで印刷を開始すると、ほんの一瞬でした(たぶん1/10秒)
- 2 kiB = 2048バイト/ 211スタックフレーム= 9.7バイト/フレーム(すべてのRAMがスタックで使用されていることを前提としていますが、実際にはそうではありません)-しかし、これは非常に合理的です。
コード:
PCアプリケーション:
/*
stack_overflow
- a quick program to force a stack overflow in order to see how many stack frames in a small function can be loaded onto the stack before the overflow occurs
By Gabriel Staples
www.ElectricRCAircraftGuy.com
Written: 6 Nov 2017
Updated: 6 Nov 2017
References:
- Jumping into C++, by Alex Allain, pg. 230 - sample code here in the chapter on recursion
To compile and run:
Compile: g++ -Wall -std=c++11 stack_overflow_1.cpp -o stack_overflow_1
Run in Linux: ./stack_overflow_1
*/
#include <iostream>
void recurse(int count)
{
std::cout << count << "\n";
recurse(count + 1);
}
int main()
{
recurse(1);
}
Arduinoの「スケッチ」プログラム:
/*
recursion_until_stack_overflow
- do a quick recursion test to see how many times I can make the call before the stack overflows
Gabriel Staples
Written: 6 Mar. 2018
Updated: 7 Mar. 2018
References:
- Jumping Into C++, by Alex Allain, Ch. 16: Recursion, p.230
*/
// Force the compiler to NOT optimize! Otherwise this recursive function below just gets optimized into a count++ type
// incrementer instead of doing actual recursion with new frames on the stack each time. This is required since we are
// trying to force stack overflow.
// - See here for all optimization levels: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
// - They include: -O1, -O2, -O3, -O0, -Os (Arduino's default I believe), -Ofast, & -Og.
// I mention `#pragma GCC optimize` in my article here: http://www.electricrcaircraftguy.com/2014/01/the-power-of-arduino.html
#pragma GCC optimize ("-O0")
void recurse(unsigned long count) // each call gets its own "count" variable in a new stack frame
{
// delay(1000);
Serial.println(count);
// It is not necessary to increment count since each function's variables are separate (so the count in each stack
// frame will be initialized one greater than the last count)
recurse (count + 1);
// GS: notice that there is no base condition; ie: this recursive function, once called, will never finish and return!
}
void setup()
{
Serial.begin(115200);
Serial.println(F("\nbegin"));
// First function call, so it starts at 1
recurse (1);
}
void loop()
{
}
参照:
- Alex AllainによるC ++へのジャンプ、16章:再帰、p.230
- http://www.electricrcaircraftguy.com/2014/01/the-power-of-arduino.html-文字通り:この「プロジェクト」で自分のウェブサイトを参照して、特定のファイルのArduinoコンパイラ最適化レベルを変更する方法を思い出させました
#pragma GCC optimize
私はそれがそこに文書化されていることを知っていたので、コマンドで。