私はCでUSBスタックに取り組んでいます。このスタックはオープンソースになり、さまざまな異なるプロジェクトで使用されます。
さまざまな構成オプションが使用可能で、コードの大きなチャンクと定数データ構造を有効または無効にします。これは組み込みプロジェクトであるため、スタックのフットプリントを最小限に抑えることが重要であり、未使用の機能を無効にすることの利点の1つは、フラッシュとRAM使用の削減です。
ビルドプロセスの出力から未使用のコードを削除するには、2つの方法があります。 #ifdef
プリプロセッサディレクティブを使用すると、コンパイラは使用されていないコードも考慮しなくなります。主な短所は、たくさんの#ifdefs
でコードを汚染し、無効なコードを壊してコンパイラの警告/エラーを取得しない可能性があることです。
他のオプションは、コードを含めることですが、リンカが最終的にそれを削除することを許可します。 GCCはこの点で非常に優れており、結果の出力は#ifdef
で削除された場合と同じサイズになります。これの欠点は、少なくとも最小レベルの最適化を有効にする必要があること(-O1
、セクション内の関数と未使用セクションの削除)であり、この動作は他のコンパイラ、または他のバージョンの保証されていませんGCC。
ここでのベストプラクティスは何ですか?その理由は?私が考慮していない利点と欠点は何ですか?
プリプロセッサディレクティブに移動します。コードを過度に汚染するべきではありません(そうする場合は、おそらくそれらのセクションをリファクタリングし、それらを個別の、おそらくインライン化された関数に抽出して、保守性を向上させることができます)。
私の意見では、最も重要な点は、コードのそのセクションが実際に使用されるかどうかが分からないということです。今日の最近のIDEは、#if ...#endifブロック内にある場合、ビルド先の構成に応じて、これらのセクションを強調表示またはぼかすことができます。これにより、そのコードの保守が容易になり、識別が容易になります。コンパイラー/オプティマイザーのみに依存すると、それだけでそれが明確になり、効率、パフォーマンス、またはプログラムサイズの点で実際的な利点はありません。
「#ifdef
またはリンカーの最適化」の正解は[〜#〜] yes [〜#〜]だと思います。
私は、開発者が最終オブジェクト自体の「大まかなトリミング」を実行できるように、利用されない可能性のある大きな主要部品(たとえば、USB 3.0サポート固有のコード)の周りに#ifdef
を使用し、リンカーに最終的な最適化を実行します。
これには、開発者が大規模なコード空間最適化をある程度制御できる利点がありますが、リンカーステージで高レベルの最適化を使用したくない場合は、高レベルの最適化を使用する必要はありません(高レベルの最適化はコードをリファクタリングして、デバッガーでシングルステップ実行すると、アセンブリー/マシンコードが何を行っているかが、記述されているCコードにほとんど似ていない)。
未使用のコードを削除して最適化することは、最も古い最適化の1つです。以前ははるかに少ないリソースしか持っていなかったコンピューターを思い出してください。今日の組み込みプラットフォームの多くは、30年前のデスクトップよりも多くのリソースを備えています。埋め込まれたツールチェーンが未使用のコードを削除しない場合、率直に言って、それはジャンクの一部です。
#ifdefsは読みにくく、保守も困難です。過去40年間に作成された、学部生ではないクラスプロジェクトではないコンパイラのほとんどすべてに遅れをとることはありません。