Arduinoでの速度と測定の問題について





この問題は、さまざまなコマンドを実行する際のArduinoのパフォーマンスの調査で発生しました(詳細については、別の記事で説明します)。 調査の過程で、オペランドの値を変更する際の個々のコマンドの動作時間の不変について疑問が生じ(後で判明しましたが、不合理ではありません)、個々のコマンドの実行時間を推定しようとすることが決定されました。 このために、一見したところ仮説を確認する小さなプログラムが作成されました(スケッチはクラスを去ると言っています)。 結論として、値16および20を観察できますが、28および32マイクロ秒さえ見つかることがあります。 受信したデータに16(MKクロック周波数)を掛けると、MKクロックサイクル(256から512)で実行時間が得られます。 残念ながら、全体像を維持しながらメインプログラムサイクルを(同じ初期データを使用して)繰り返し実行すると、実行時間の分布が異なるため、実際の時間変動は初期データに関係しません。 元の仮説は反証されますが、それは興味深いものになり、まさにそのような重要な広がりの理由は何ですか。



必要な注意-コマンドの実行時間を測定するには、より洗練されたプログラムを使用する必要があることをよく理解していますが、大まかな見積もりについては、後で説明するもので十分です。



したがって、時間は変化しています。非常に重要なことは、この現象の原因を探していることです。 まず、取得した値の多様性に注意を払い、時間の経過とともに作業ライブラリの説明を見て、4µsecが測定量であることを確認します。したがって、quantaに移動して、4または5(非常に頻繁)および6または7または8(非常にまれな)ユニット。 前半では、すべてが簡単です。測定値が4〜5ユニットの間にある場合、散布は避けられなくなります。 さらに、サンプルが独立していると考えると、統計的手法によって測定精度を高めることができます。



しかし、後半(6.7.8)では事態はさらに悪化しています。 スプレッドはソースデータと相関しないことがわかりました。つまり、これはコマンドの実行時間に影響する他のプロセスの現れです。 排出量はかなりまれであり、計算された平均値に大きな影響を及ぼさないことに注意してください。 それらをまったく無視することは可能ですが、これは私たちのスタイルではありません。 一般的に、長年のエンジニアリングの仕事で、最も不適当な瞬間に背中(うまい、または他の場所)を攻撃するうんざりする能力があるので、どんなに些細なことでも誤解を残すことはできないことに気付きました。



私たちは仮説1を提唱し始めます-最も便利な(利便性と汎用性において、それはCreatorの直接的な介入に次ぐ)-もちろん、ソフトウェアの不具合は、私のものではなく、私のプログラムは決して失敗しませんが、接続されたライブラリ(コンパイラ、オペレーティングシステム、ブラウザなど) 。-必要なものを置き換えます)。 さらに、 www.tinkercad.comのエミュレーターでプログラムを実行しているため、ソースが利用できないため、エミュレーターのバグを参照してトピックを閉じることができます。 この仮説の短所:



  1. サイクルごとに、逸脱の場所が変化し、それが示唆されます。
  2. このサイトはまだAutoDeskをサポートしていますが、議論はやや弱いです。
  3. 「私たちは、起こっていることは幻覚ではないという仮定を受け入れました。そうでなければ、単に面白くないでしょう。」


次の仮定は、測定結果に対するいくつかのバックグラウンドプロセスの影響です。 しかし、私たちは何もしていないように思われますが、結果をシリアルで出力しています。 仮説2が発生します-出力時間がコマンドの実行時間に追加されることがあります(奇妙なことですが...) 出力がどれだけあるかは疑わしいですが、とにかく-Flushを追加しても効果がなく、出力を終了するための遅延を追加しても効果がありませんでした。一般に出力をループから移動します-とにかく、時間のジャンプ-これは間違いなくシリアルではありません



さて、残っているのはサイクル自体の組織であり(その期間を変更することは恐怖であり、明確ではありません)、それはすべてです... 私はこの関数の最初の呼び出しと2番目の呼び出しの実行時間が同じであることを意味し、これら2つの値を減算するとゼロになりますが、この仮定が間違っている場合はどうなりますか?



仮説3-タイムカウントの2回目の呼び出しが最初のコールより長くかかる場合や、タイムカウントに関連付けられたアクションが結果に影響する場合があります。 時間を操作する機能のソースコードを見る(arduino-1.8.4 \ hardware \ arduino \ avr \ cores \ arduino \ lighting.c-私はそのようなことに対する態度を繰り返し表明しました、私は繰り返しません)カウンタの下部のハードウェア増加のサイクルが中断され、カウンタの上部が増分されます。



サイクルの実行時間は4〜5であるため、170 *(4..5)/ 256 = 170の測定セグメントで3〜4の異常値が予想されます。 私たちは見ています-それは非常によく似ており、実際には4つあります。 最初の理由と2番目の理由を区別するために、割り込みが禁止されているクリティカルセクションで計算を行います。 結果はあまり変わらず、排出量にはまだ場所があります。つまり、micros()呼び出しによって余分な時間がもたらされます。 ここでは何もできませんが、ソースコードは利用可能ですが、変更することはできません。ライブラリはバイナリに含まれています。 もちろん、時間を操作する独自の関数を作成してその動作を監視することもできますが、もっと簡単な方法があります。



「長時間」の割り込み処理は、期間の増加の考えられる理由であるため、測定プロセス中に発生する可能性を排除します。 これを行うには、その発現を待ってから、測定サイクルを実行します。 中断は測定サイクルが続くよりもはるかに少ないため、中断がないことを保証できます。 プログラムの対応するフラグメントを作成し(ソースコードから抽出した情報を使用したダーティハックを使用)、「これはストリートマジックです」すべてが正常になります-PT 166クロックサイクルの加算操作の実行時間の平均値で4および5クォンタムの実行時間を測定します以前に測定された値に対応します。 仮説は確認済みとみなすことができます。



もう1つの質問が残っています-中断に時間がかかるのは何ですか?

(7.8)-(5)〜2クォンタム= * 4 = 8ミリ秒* 16 = 128プロセッササイクル? ソースコード(つまり、godbolt.comのコンパイラーによって生成されたアセンブラーコード)に目を向けると、割り込み自体が約70サイクル、そのうち60が絶えず実行されていることがわかります。中断-受け取ったものより少ないが、十分に近い。 その違いは、コンパイラまたはそれらの使用モードの違いに起因します。



さて、ここでさまざまな引数を使用してPT追加コマンドの実際の実行時間を測定し、引数が変更されたときに実際に大幅に変更されることを確認できます: 99.9%の確率で、これはこの特定のライブラリでのアライメントとその実装の機能の必要性によるものですが、この研究は明らかに検討中の問題の範囲を超えています。



付録-プログラムテキスト:
void setup() { Serial.begin(9600); } volatile float t; //   void loop() { int d[170]; unsigned long time,time1; float dt=1/170.; for (int i=0; i<170; ++i) { { //       time1=micros(); long time2; do { time2=micros(); } while ((time2 & ~0xFF) == (time1 & ~0xFF)); }; /**/ time1=micros(); //   /* cli(); //       -   */ t=10.63; //     t=t+dt; //   /* sei(); //    */ time = micros(); //   time1=time-time1; d[i]=time1/4; /* Serial.print(time1); //      Serial.flush(); //     Delay(20); //    */ }; //   ,     float sum=0; for (int i=0; i<170; ++i) { sum+=d[i]; Serial.println(d[i]); }; Serial.println((sum/170-2.11)*4*16); //2.11Serial.flush(); //    ,     }
      
      








All Articles