より高速なプロセッサ/クロックでより多くのコードを実行できますか?


9

私は16Mhzで動作するATmega 328で実行するプログラムを書いています(ご存知ならArduino Duemilanove、それはAVRチップです)。

100マイクロ秒ごとに実行される割り込みプロセスがあります。100マイクロ秒の1ループで実行できる「コード」の量を計算するのは不可能だと思います(おそらくアセンブリに変換されてからバイナリイメージに変換されるCで書いています)。

また、これはコードの複雑さに依存します(たとえば、巨大な1つのライナーは、いくつかの短い行よりも実行が遅くなる可能性があります)。

私の理解は正しいですか、クロックレートまたは16Mhzのプロセッサは1秒あたり1600万サイクルを実行します(つまり、1マイクロ秒あたり16サイクル16,000,000 / 1,000 / 1,000)。したがって、100マイクロ秒のループでさらに多くのことをしたい場合、72Mhzバージョンのようなより高速なモデルを購入すると、マイクロ秒あたり72サイクル(72,000,000 / 1,000 / 1,000)になりますか?

現在のところ、実行が少し遅すぎます。つまり、ループを実行するのに100マイクロ秒より少し長くかかります(正確にはどれだけ長く言うのが難しいか、段階的に遅れます)。もう少し実行したいのですが、これはより速いチップを得るための健全なアプローチですか、それとも私は怒っていますか?


.... ATmega328はARMチップではありません。それはAVRです。
vicatcu

乾杯、修正しました!
jwbensley 2010年

回答:


9

一般に、デバイスが1秒間に実行できるアセンブリ命令の数は、命令の組み合わせと、各命令タイプの実行にかかるサイクル数(CPI)によって異なります。理論的には、逆アセンブルされたasmファイルを調べ、問題のある関数を調べ、その中のさまざまな種類の命令をすべてカウントし、ターゲットプロセッサのデータシートからサイクルカウントを調べることで、コードをサイクルカウントできます。

1秒あたりの有効な命令数を決定するという問題は、より複雑なプロセッサでは、パイプライン化されており、キャッシュがあり、キャッシュがないという事実により悪化します。これは、フライトプロセッサの単一の命令であるATMega328のような単純なデバイスには当てはまりません。

実際問題として、AVRのような単純なデバイスの場合、私の答えは多かれ少なかれ「はい」でしょう。クロック速度を2倍にすると、特定の関数の実行時間が半分になります。ただし、AVRの場合、20MHzより速く動作しないため、Arduinoをさらに4MHzだけ「オーバークロック」することができます。

このアドバイスは、より高度な機能を持つプロセッサに一般化されませ。Intelプロセッサのクロック速度を2倍にしても、実際には1秒あたりに実行される命令数が2倍になることはありません(分岐の予測ミス、キャッシュミスなどのため)。


こんにちは、有益な回答をありがとう!これらの1つを見たことがあります(coolcomponents.co.uk/catalog/product_info.php?products_id=808)。AVRは20Mhzより速くできないので、なぜですか?上記のボード上のチップ(uk.farnell.com/stmicroelectronics/stm32f103rbt6/…)は72Mhz ARMです。これにより、上記で説明したように、適切なパフォーマンスの向上が期待できますか?
jwbensley、2011年

2
処理速度を2倍にして、フラッシュから命令をフェッチできる速度を超え始める可能性があるため、命令のスループットは向上しません。この時点で、CPUがフラッシュからの命令の到着を待機する間、CPUが一時停止する「フラッシュ待機状態」を開始します。一部のマイクロコントローラは、フラッシュよりもはるかに高速なRAMからコードを実行できるようにすることでこれを回避します。
Majenko、2011年

@Majenko:おかしい、私達は同時に同じポイントを作りました。
Jason S、

それは起こります...あなたのものは私のものより優れています:)
Majenko

1
わかりました、私はビカットの答えを「答え」としてマークしました。パフォーマンスに関するスピードに関する私の元の質問に関しては、それが最も適切だったと思います。彼らは私が最初に実現したよりも広い主題であることを私に示しました、そしてそれで、彼らは私にたくさん教えてくれ、研究にたくさんを与えてくれます。だからみんなに感謝します:D
jwbensley

8

@vicatcuの答えはかなり包括的です。注意すべきもう1つの点は、プログラムやデータメモリを含む I / Oにアクセスするときに、CPUが待機状態(CPUサイクルの停止)に陥ることがあることです。

たとえば、TI F28335 DSPを使用しています。RAMの一部の領域は、プログラムおよびデータメモリの0待機状態であるため、RAMでコードを実行すると、命令ごとに1サイクルで実行されます(1サイクル以上かかる命令を除く)。ただし、フラッシュメモリ(内蔵EEPROM、多かれ少なかれ)からコードを実行する場合、150MHzでフルに実行することはできず、数倍遅くなります。


高速割り込みコードに関しては、多くのことを学ぶ必要があります。

まず、コンパイラに慣れます。コンパイラーが適切に機能する場合、ほとんどの場合、手作業でコード化したアセンブリよりも遅くなることはありません。(「それよりもはるかに遅い」:2の要素は私には問題ありませんが、10の要素は受け入れられません)コンパイラ最適化フラグを使用する方法(およびいつ)を学習する必要があります。コンパイラーの出力で、その動作を確認します。

コードを高速化するためにコンパイラーにできるその他のこと:

  • 小さな関数と、1回または2回だけ実行される関数の両方で、インライン関数を使用します(Cがこれをサポートしているかどうか、またはC ++-ismのみかどうかを思い出せません)。欠点は、特にコンパイラの最適化がオンになっている場合、インライン関数のデバッグが難しいことです。ただし、特に「関数」の抽象化がコードの実装ではなく概念設計の目的である場合は、不要な呼び出し/戻りシーケンスを節約できます。

  • コンパイラーのマニュアルを調べて、組み込み関数があるかどうかを確認してください。これらはコンパイラーに依存する組み込み関数であり、プロセッサーのアセンブリー命令に直接マップされます。一部のプロセッサには、最小/最大/ビット反転などの便利なことを行うアセンブリ命令があり、そうすることで時間を節約できます。

  • 数値計算を行う場合は、数学ライブラリ関数を不必要に呼び出さないようにしてください。コードがy = (y+1) % 4、期間が4のカウンターのようなもので、コンパイラーがモジュロ4をビットANDとして実装することを期待しているケースが1つありました。代わりに、数学ライブラリを呼び出しました。だから私たちy = (y+1) & 3は、私たちがやりたいことをするために置き換えました。

  • ちょっとしたハックページに慣れてください。これらのうち少なくとも1つを頻繁に使用することを保証します。

また、CPUのタイマーペリフェラルを使用してコード実行時間を測定する必要があります。それらのほとんどには、CPUクロック周波数で実行するように設定できるタイマー/カウンターがあります。重要なコードの最初と最後でカウンターのコピーをキャプチャして、どれだけ時間がかかるかを確認できます。それができない場合は、別の方法として、コードの先頭で出力ピンを下げ、最後に上げて、オシロスコープでこの出力を見て実行時間を計ることができます。各アプローチにはトレードオフがあります。内部タイマー/カウンターはより柔軟です(いくつかの時間を計ることができます)が、情報を取得することは困難ですが、出力ピンの設定/クリアはスコープですぐに表示され、統計をキャプチャできますが、複数のイベントを区別するのは困難です。

最後に、経験が付属して非常に重要なスキルがあります-一般的および特定のプロセッサ/コンパイラの組み合わせで両方:ときといないときに最適化することを知ること。一般に、答えは最適化しないことです。Donald Knuthの引用はStackOverflowに頻繁に投稿されます(通常は最後の部分のみ)。

小さな効率については忘れてください。たとえば、約97%の時間です。時期尚早な最適化がすべての悪の根源です

しかし、ある種の最適化を行う必要があることがわかっている状況にいるので、弾丸を噛んで最適化(またはより高速なプロセッサ、またはその両方)を行う時が来ました。ISR全体をアセンブリで記述しないでください。これはほぼ保証された災害です。これを行うと、数か月または数週間以内に、行ったことの一部とその理由が忘れられ、コードは非常に壊れやすく、変更が困難になります。それは、しかし、あなたのコードの一部である可能性が高いがありますアセンブリのための良い候補。

コードの一部がアセンブリコーディングに適していることを示します。

  • よく含まれ、明確に定義された小さなルーチンであり、変更される可能性が低い
  • 特定のアセンブリ命令を利用できる関数(最小/最大/右シフトなど)
  • 何度も呼び出される関数(乗数が得られます:各呼び出しで0.5usecを保存し、10回呼び出されると、5 usecを節約できます)

C呼び出し可能なアセンブリルーチンを記述できるように、コンパイラの関数呼び出し規約(たとえば、引数をレジスタに配置する場所、およびレジスタが保存/復元する場所)を学習します。

私の現在のプロジェクトでは、10kHzの割り込みで実行する必要がある重要なコード(100usec-なじみのある?)を備えたかなり大きなコードベースがあり、アセンブリで記述された関数はそれほど多くありません。つまり、CRC計算、ソフトウェアキュー、ADCゲイン/オフセット補償などです。

幸運を!


経験的実行時間測定手法に関する良いアドバイス
vicatcu

私の質問に対するもう1つの素晴らしい答えです。この素晴らしい知識の塊にJason Sに感謝します。これを読んだ後、2つのことが明らかになります。1つ目は、100uSから500uSに割り込みを発生させて、コードを実行する時間を増やすことができます。第二に、私のコードは効率が高すぎると思います。割り込み時間が長く、コードが優れているため、すべて問題ないかもしれません。Stackoverflowはコードを投稿するためのより良い場所なので、そこに投稿し、リンクをここに配置します。もし誰かが見てもらい、推奨をしたい場合は、次のようにしてください:D
jwbensley

5

注意すべきもう1つのこと-コードをより効率的にするために実行できるいくつかの最適化があるでしょう。

たとえば、タイマー割り込み内から実行されるルーチンがあります。ルーチンは52µS以内に完了する必要があり、実行中に大量のメモリをステップスルーする必要があります。

メインカウンター変数をレジスターにロックすることで大幅な速度の増加を管理しました(私のµCとコンパイラーでは-使用しているユーザーによって異なります)。

register unsigned int pointer asm("W9");

コンパイラの形式はわかりません-RTFMですが、アセンブリに切り替えずにルーチンを高速化する方法があります。

そうは言っても、ルーチンの最適化ではコンパイラーよりもはるかに優れた仕事ができるため、アセンブリーに切り替えると速度が大幅に向上する可能性があります。


lolアセンブラのチューニングとレジスタの割り当てに関する自分の答えに「同時に」コメントしました:)
vicatcu '28

16 MHzプロセッサで100usを使用している場合、明らかに非常に巨大であるため、最適化するコードがたくさんあります。今日のコンパイラは手作業で最適化されたアセンブリの約1.1倍のコードを生成すると聞いています。そのような巨大なルーチンにはまったく価値がありません。6行機能を20%削減するために、おそらく...
DefenestrationDay

1
必ずしもではありません...ループ内のコードはわずか5行です。そして、それはコードサイズではなく、コード効率に関するものです。別の方法でコードを記述して、より高速に実行できる場合があります。私は割り込みルーチンを知っていました。たとえば、速度のためにサイズを犠牲にします。同じコードを10回連続して実行することにより、ループを実行するコードとそれに関連するカウンター変数の時間を節約できます。はい、コードは10倍長くなりますが、実行速度は速くなります。
Majenko、2011年

こんにちはマジェンコ、私はアセンブリを知りませんが、私はそれを学ぶことを考えていました、そしてArduinoは私のデスクトップコンピュータよりも複雑ではなくなるので、特に私が知りたいので、これは学ぶ良い機会かもしれないと思っていました何が起こっているのか、そしてより低いレベルについて。他の人が言ったように、私は全部を特定の部分だけ書き直すことはしません。私の理解では、C内でASMに出入りできます。これは正しいですか、これは、CとASMのこの組み合わせをどのように実現できるのでしょうか?詳細については、一般的なアイデアの直後に、stackoverflowに投稿します。
jwbensley 2011年

@javano:はい。C内でASMにドロップインおよびドロップアウトできます。多くの組み込みシステムは、Cとアセンブリの混合でそのように記述されていました。主に、時間。ただし、gcc(Arduinoで使用されるコンパイラー)などの最新のCコンパイラーは、アセンブリ言語を必要とするほとんどすべての場合と多くの場合すべてを処理します。
davidcary 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.