新しい機能を必要とするレガシー ファームウェア アプリケーションがあります。アプリケーションのサイズは、すでにデバイスの限られたフラッシュ容量に近づき、いくつかの新しい機能と変数が限界を超えていました。コンパイラーの最適化を有効にするとうまくいきますが、顧客は過去に失敗を引き起こしたことがあるため、そうすることに慎重です。では、C コードをリファクタリングしてより小さな出力を生成するときに探すべき一般的なことは何ですか?
7 に答える
- 可能であれば、データ テーブルの代わりに生成関数を使用する
- インライン関数を無効にする
- よく使うマクロを関数化
- ネイティブ マシン サイズより大きい変数の解像度を下げる (つまり、8 ビット マイクロ、16 ビットと 32 ビットの変数を取り除くようにします - 一部のコード シーケンスを 2 倍または 4 倍にします)。
- マイクロの命令セットが小さい場合 (アーム サム)、コンパイラで有効にします。
- メモリがセグメント化されている場合 (つまり、ページ化または非線形)、
- より少ないグローバル呼び出し (より大きな呼び出し命令) を使用する必要があるように、コードを再配置します。
- コードと変数の使用方法を再編成して、グローバル メモリ呼び出しを排除します
- グローバル メモリの使用量を再評価します。スタックに配置できる場合は、はるかに優れています。
- デバッグをオフにしてコンパイルしていることを確認してください - 一部のプロセッサでは大きな違いが生じます
- その場で生成できないデータを圧縮し、高速アクセスのために起動時に RAM に解凍します
- コンパイラ オプションを掘り下げる - すべての呼び出しが自動的にグローバルになる可能性がありますが、ファイルごとに安全に無効にしてサイズを (場合によっては大幅に) 削減できる場合があります。
有効にした場合よりも多くのスペースが必要な場合compile with optimizations
は、生成されたアセンブリと最適化されていないコードを比較してください。次に、最大の変更が行われたコードを書き直して、最適化をオフにしてトリッキーな C の書き直しに基づいてコンパイラが同じ最適化を生成するようにします。
たとえば、同様の比較を行う複数の「if」ステートメントがある場合があります。
if(A && B && (C || D)){}
if(A && !B && (C || D)){}
if(!A && B && (C || D)){}
次に、新しい変数を作成し、事前にいくつかの比較を行うことで、コンパイラがコードを複製するのを防ぐことができます。
E = (C || D);
if(A && B && E){}
if(A && !B && E){}
if(!A && B && E){}
これは、オンにした場合にコンパイラが自動的に行う最適化の 1 つです。他にもたくさんあります。C コードでこれを手動で行う方法を学びたい場合は、コンパイラ理論を少し読むことを検討してください。
重複するコードをリファクタリングすることは、プログラムのメモリ フットプリントに最大の影響を与えるはずです。
上記の回答は、「コンパイラの最適化をオンにする[コードサイズを縮小する]」と主張しています。組込みシステムの TI DSP プログラミングに関するすべてのドキュメントと経験から、最適化をオンにするとコード サイズが増加することはわかっています (TI DSP チップの場合)。
説明させてください:
TI TMSCx6416 DSP には、コード サイズに影響する 9 つのコンパイラ フラグがあります。
- 最適化のための 3 つの異なるフラグ
- デバッグ用の 3 つの異なるフラグ
- コード サイズの 3 つの異なるフラグ
私のコンパイラでは、最適化レベル 3 をオンにすると、ドキュメントには次のように記載されています。
- 特定の関数の自動インライン化が発生します --> コード サイズが増加します
- ソフトウェア パイプラインが有効になっている --> コード サイズが増加します
ソフトウェア パイプライン処理とは
これは、コンパイラがアセンブリで for ループの実行を大幅に高速化する処理を実行する場所です (最大で数倍高速化します) が、コード サイズが大きくなります。ウィキペディアでソフトウェア パイプラインについて読むことをお勧めします ( loop unrolling、prolog、および epilog を探してください)。
したがって、ドキュメントをチェックして、最適化によってコードが大きくなっていないことを確認してください。
もう 1 つの提案は、コード サイズに関連するコンパイラ フラグを探すことです。コード サイズのコンパイラ フラグがある場合は、それらを最高の設定に上げてください。通常、コードサイズのためにコンパイルすると、コードの実行速度が遅くなります...しかし、そうしなければならない場合があります。
マクロに注意してください。たった 1 つのマクロ展開から多くのコードを生成できます。そのようなマクロを見つけた場合は、それらのサイズを最小化し、機能を関数に移動するように書き直してみてください。
重複コードに注意してください - コピー&ペーストされたものと論理的に重複したものの両方。重複するコードを関数に分割してみてください。
コンパイラがインライン化をサポートしているかどうかを確認し、無効にすることができます。
バグを引き起こすコンパイラの最適化? それは奇妙だ。プログラムのマップを取得し、データまたはコードをターゲットにする必要があるかどうかを確認します。重複したコードを探します。同様の目的を持つコードを探します。その 1 つの例は、メモリ フットプリントを小さくすることを目的とした、busybox コードです。
読みやすさよりもサイズを重視しているため、 goto などで非常に見苦しくなることがあります。