開発およびデバッグ段階で最適化を無効にするのは本当に良い習慣ですか?


15

私はCでの16ビットPICマイクロコントローラーのプログラミングを読みました。

ただし、プロジェクトの開発およびデバッグ段階では、分析中のコードの構造を変更し、シングルステップやブレークポイントの配置に問題を引き起こす可能性があるため、すべての最適化を無効にすることをお勧めします。

私は少し混乱したと告白します。著者がC30評価期間のためにそれを言ったのか、それが本当に良い習慣であるのかを私は理解していませんでした。

このプラクティスを実際に使用しているかどうか、そしてその理由を知りたいのですが。

回答:


16

これはソフトウェアエンジニアリング全体としてかなり標準的なものです。コードを最適化すると、コンパイラーは、操作の違いが分からない限り、必要に応じてさまざまなものを再配置することができます。そのため、たとえば、ループのすべての繰り返し内で変数を初期化し、ループ内で変数を変更しない場合、オプティマイザーはその初期化をループ外に移動できるため、時間を無駄にすることはありません。

また、上書きする前に何も行わない数を計算することを認識するかもしれません。その場合、無駄な計算がなくなる可能性があります。

最適化の問題は、オプティマイザーが移動または削除したコードの一部にブレークポイントを配置する必要があることです。その場合、デバッガーは必要な処理を実行できません(通常、ブレークポイントをどこかに近づけます)。したがって、生成されたコードを作成したコードにより近くするために、デバッグ中に最適化をオフにします。これにより、ブレークしたいコードが実際に存在することが保証されます。

ただし、コードによっては最適化によって問題が生じる可能性があるため、これには注意が必要です。一般に、正しく機能するオプティマイザーによって壊れるコードは、実際には何かを逃しているバグの多いコードです。そのため、通常、オプティマイザーが壊れる理由を把握する必要があります。


5
この引数への反論は、オプティマイザーが物事をより小さくおよび/またはより速くする可能性が高いことであり、タイミングやサイズに制約のあるコードがある場合は、最適化を無効にして何かを壊し、問題のデバッグに時間を浪費する可能性があります本当に存在します。もちろん、デバッガーはコードをより遅く、より大きくすることもあります。
ケビンフェルメール

これについてどこでもっと知ることができますか?
ダニエルグリロ

私はC30コンパイラーを使用したことはありませんが、C18コンパイラーについては、サポートする最適化について説明したコンパイラー向けのアプリノート/マニュアルがありました。
マーク

@O Engenheiro:サポートしている最適化については、コンパイラーのドキュメントを確認してください。最適化は、コンパイラ、ライブラリ、およびターゲットアーキテクチャによって大きく異なります。
マイケルコーネ

繰り返しますが、C30コンパイラ用ではありませんが、gccは適用できるさまざまな最適化の長いリストを公開しています。また、このリストを使用して、特定の制御構造をそのまま保持したい場合に、きめ細かな最適化を行うこともできます。一覧はこちら:gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
ケビン・フェルメール

7

この質問をJack Ganssleに送りました。これが彼の答えです。

ダニエル、

私は、リリースされたコードで最適化が行われるものを使用してデバッグすることを好みます。NASAは「あなたが飛ぶものをテストし、あなたがテストするものを飛ばす」と言います。つまり、テストを行ってからコードを変更しないでください!

ただし、デバッガーが機能するように最適化をオフにする必要がある場合があります。作業中のモジュールだけでそれらをオフにしようとしています。そのため、数百行程度のコードなど、ファイルを小さくすることをお勧めします。

ジャック、最高


この回答の2つの段落には明確な区別がないと思います。テストとは、ソフトウェア、ファームウェア、および/またはハードウェアが適切に機能することを実証するための手順を指します。デバッグとは、コードが命令ごとにステップ実行されて、なぜまだ動作していないのかを確認するプロセスです。
ケビンフェルメール

選択肢があると便利です。そのため、テストは、最適化の有無にかかわらず、より多くの品種/順列をカバーできます。より多くのカバレッジ、より良いテスト

@reemrevnivek、あなたがデバッグしているとき、あなたもテストしていませんか?
ダニエルグリロ

@O Engenheiro-いいえ。テストが失敗した場合にのみデバッグします。
ケビンフェルメール

6

依存し、これは一般的にC30だけでなくすべてのツールに当てはまります。

最適化は、多くの場合、さまざまな方法でコードを削除および/または再構築します。switchステートメントはif / elseコンストラクトで再実装されるか、場合によってはすべて削除される可能性があります。y = x * 16は一連の左シフトなどに置き換えられる可能性がありますが、この最後のタイプの最適化は通常ステップスルーできますが、ほとんどはyaを取得する制御ステートメントの再構築です。

これにより、Cで定義した構造がもはや存在しないため、デバッガーをCコードでステップ実行できなくなります。これらの構造は、コンパイラーによって置き換えられるか、コンパイラーがより高速であるか、スペースを節約すると信じられるものに並べ替えられたためです。また、ブレークする命令が存在しない可能性があるため、Cリストからブレークポイントを設定できないようにすることもできます。たとえば、ifステートメント内にブレークポイントを設定しようとしても、コンパイラがそのifを削除した可能性があります。whileループまたはforループ内にブレークポイントを設定しようとする場合がありますが、コンパイラーはループが存在しないようにそのループを展開することにしました。

このため、最適化をオフにしてデバッグできる場合、通常は簡単です。常に最適化をオンにして再テストする必要があります。これは、重要volatileなエラーを見逃し、断続的な障害(またはその他の奇妙な現象)を引き起こすことがわかる唯一の方法です。

組み込み開発の場合、とにかく最適化に注意する必要があります。具体的には、タイミングが重要なコードのセクション、たとえばいくつかの割り込み。これらの場合、アセンブリのクリティカルビットをコーディングするか、コンパイラディレクティブを使用して、これらのセクションが最適化されないようにして、実行時間が固定または最悪の場合の実行時間が固定されるようにします。

他の落とし穴はコードをuCに適合させることができるため、コードをチップに単純に適合させるためにコード密度の最適化が必要になる場合があります。これは、通常、ファミリ内で最大のROM容量uCから開始し、コードがロックされた後、製造用に小さいもののみを選択することをお勧めする理由の1つです。


5

一般的に、リリースする予定の設定でデバッグします。最適化されたコードをリリースする場合、最適化されたコードでデバッグします。最適化されていないコードをリリースする場合、最適化されていないコードでデバッグします。これには2つの理由があります。第一に、オプティマイザーは、最終製品が最適化されていないコードと異なる動作をするのに十分なタイミングの違いを大きくすることができます。第二に、ほとんどは非常に優れていますが、コンパイラベンダーは間違いを犯し、最適化されたコードは最適化されていないコードとは異なる結果を生成する可能性があります。その結果、リリースする予定の設定で可能な限り多くのテスト時間を取得したいです。

そうは言っても、前の回答で述べたように、オプティマイザーはデバッグを困難にする可能性があります。デバッグが困難なコードの特定のセクションが見つかった場合、オプティマイザーを一時的にオフにし、デバッグを実行してコードを機能させ、オプティマイザーをオンに戻してもう一度テストします。


1
ただし、最適化を有効にすると、コードをシングルステップ実行することはほとんど不可能になります。最適化をオフにしてデバッグし、リリースコードを使用してユニットテストを実行します。
Rocketmagnet

3

私の通常の戦略は、最終的な最適化(適切なサイズまたは速度の最大値)で開発することですが、何かトレースをデバッグする必要がある場合、最適化を一時的にオフにします。これにより、最適化レベルの変更の結果として生じるバグのリスクが軽減されます。

典型的な障害モードは、必要に応じて変数が揮発性であると宣言していないために、最適化を増やすと以前は見えなかったバグが表面化する場合です。


2

リリースするフォーム、デバッガー、デバッグ用のコンパイルを使用して、リリース用にコンパイルするまで表示されない多くのバグ(A LOT)を隠します。その時までに、これらのバグを見つけることは、あなたが行くにつれてデバッグするのと比べてはるかに困難です。20年経った今、私はgdbやその他のデバッガーを使用したことがなく、変数やシングルステップを監視する必要がありませんでした。1日あたり数百から数千の回線。だからそれは可能です、そうでないと考えるように導かれてはいけません。

デバッグ用にコンパイルしてからリリース用にコンパイルすると、2〜2倍以上の労力がかかります。バインドして、デバッガーなどのツールを使用する必要がある場合は、特定の問題を解決するためにデバッガー用にコンパイルし、通常の操作に戻ります。

オプティマイザーがコードを高速化するなど、他の問題も当てはまります。特に、コンパイラーオプションを使用してタイミングの変更を組み込み、プログラムの機能に影響を与える可能性があるため、ここでもフェーズ全体で成果物のコンパイルの選択を使用します。コンパイラーもプログラムであり、バグがあり、オプティマイザーは間違いを犯します。その場合、最適化せずにコンパイルしても何も問題がない場合は、常にそのようにしてください。私が好むパスは、最適化のためにコンパイルすることです。コンパイラの問題が疑われる場合は、最適化を無効にします。それを修正すると、通常はアセンブラ出力を調べて理由を把握することができます。


1
良い答えを詳しく説明するために+1する:多くの場合、「デバッグ」モードでコンパイルすると、変数の周りのスタック/ヒープに未割り当てスペースが埋め込まれ、小さな書き捨てエラーと文字列エラーが軽減されます。リリースでコンパイルすると、実行時のクラッシュが頻繁に発生します。
モーテンジェンセン

2

私は常に-O0(最適化をオフにするgccオプション)を使用してコードを開発しています。リリースに向けて物事を進めたいと思っている時点で、-Os(サイズの最適化)で開始します。一般に、キャッシュに保持できるコードが多いほど良いので、超最適化されていなくても。

gdbは-O0コードを使用するとはるかにうまく機能し、アセンブリにステップインしなければならない場合は従うのがはるかに簡単であることがわかります。-O0と-Osを切り替えると、コンパイラーがコードに対して行っている処理を確認することもできます。それは時々非常に興味深い教育であり、コンパイラのバグを発見することもできます...あなたの髪の毛を引き出してコードの何が悪いのかを理解しようとする厄介なもの!

本当に必要な場合は、-fdata-sectionsと-fcode-sectionsに--gc-sectionsを追加し始めます。これにより、実際に使用されていない関数とデータセグメント全体をリンカーが削除できます。物事をさらに縮小したり、物事をより速くしようとするためにいじくり回すことができる小さなものがたくさんありますが、概してこれらは私が使用することになってしまう唯一のトリックであり、私は手渡します-組み立てる。


2

はい。デバッグ中に最適化を無効にすることは、次の3つの理由から、しばらくの間ベストプラクティスでした。

  • (a)高レベルデバッガーでプログラムをシングルステップで実行する場合、混乱が少し少なくなります。
  • (a)(廃止)アセンブリ言語デバッガーを使用してプログラムをシングルステップデバッグする場合、混乱ははるかに少なくなります。(しかし、高レベルのデバッガを使用できるのに、なぜこれを気にするのですか?)
  • (b)(長い間廃止されている)この特定の実行可能ファイルを一度だけ実行してから、変更を加えて再コンパイルします。コンパイラーがこの特定の実行可能ファイルを「最適化」している間、10分未満の実行時間を節約しようとすると、さらに10分間待つのは時間の無駄です。(これは、2秒未満で完全な最適化を使用して、典型的なマイクロコントローラー実行可能ファイルをコンパイルできる最新のPCとは関係ありません)。

多くの人々はこの方向にさらに進み 、アサーションをオンにして出荷します。


アセンブリコードのシングルステップ実行は、ソースコードが実際に外観とは異なるもの(例: "longvar1&=〜0x40000000; longvar2&=〜0x80000000;")またはコンパイラーがバグのあるコードを生成する場合の診断に非常に役立ちます。 。マシンコードデバッガーを使用していくつかの問題を突き止めましたが、他の方法で突き止めることはできなかったと思います。
-supercat

2

シンプル:最適化は時間がかかるため、開発中にそのコードを変更する必要がある場合は役に立たない可能性があります。そのため、時間とお金の無駄になる可能性があります。
ただし、完成したモジュールには役立ちます。おそらく変更を必要としないコードの部分。


2

コンパイラは実際にメモリに影響しない多くのステートメントを削除できるため、ブレークポイントの場合には確かに意味があります。

次のようなものを検討してください:

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

完全に最適化することができます(i読んだことがないため)。ブレークポイントの観点から見ると、基本的にコードがすべて存在していなかったときに、すべてのコードがスキップされたように見えます。

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;

1

デバッガを使用している場合、最適化を無効にしてデバッグを有効にします。

個人的には、PICデバッガーは修正に役立つ以上の問題を引き起こすことがわかりました。
USARTに対してprintf()を使用して、C18で記述されたプログラムをデバッグします。


1

コンパイルで最適化を有効にすることに対する議論のほとんどは次のようになります。

  1. デバッグのトラブル(JTAG接続、ブレークポイントなど)
  2. 間違ったソフトウェアのタイミング
  3. sh * tは正常に動作しなくなります

私見最初の2つは合法であり、3番目はそれほどではありません。多くの場合、あなたはいくつかの悪いコードを持っているか、言語/実装の安全でない悪用に依存しているか、著者は単に古き良きおじさんの未定義行動のファンであるかもしれません。

Embedded in Academiaブログには未定義の振る舞いについて1つまたは2つのことが書かれています。この投稿はコンパイラーがそれをどのように活用するかについてです:http : //blog.regehr.org/archives/761


別の考えられる理由は、最適化がオンになっていると、コンパイラがうっとうしいほど遅くなる可能性があることです。
supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.