コンパイルされたコードのサイズを縮小するためのリファクタリング方法にはどのようなものがありますか?

  • 可能であれば、データ テーブルの代わりに生成関数を使用する
  • インライン関数を無効にする
  • よく使うマクロを関数に変換
  • ネイティブ マシン サイズよりも大きい変数の解像度を下げます (つまり、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 コードでこれを手動で行う方法を学びたい場合は、コンパイラ理論を少し読むことを検討してください。


一般的に:リンカーマップまたはツールを使用して、最大/最も多数のシンボルを把握し、逆アセンブラーを使用してそれらを調べます。この方法で見つけたものには驚かれることでしょう。

少しの perl などを使用すると、.xMAP ファイルまたは「objdump」または「nm」の結果を短時間で処理し、関連情報のためにさまざまな方法で再ソートできます。

小規模な命令セットに固有:リテラル プールの使用に注意してください。から変更しながら。 THUMB (命令ごとに 16 ビット) 命令セットに対する ARM (命令ごとに 32 ビット) 命令セットは、一部の ARM プロセッサで役立ちます。これにより、「即値」フィールドのサイズが縮小されます。

突然、グローバルまたは静的からの直接的な負荷が非常に間接的になります。命令で直接アドレスをエンコードするのではなく、最初にグローバル/スタティックのアドレスをレジスタにロードしてから、そこからロードする必要があります。したがって、いくつかの追加の指示が表示されますおよび 通常は 1 つの命令であったものの、リテラル プール内の追加のエントリ。

これに対抗する戦略は、グローバルとスタティックを構造にグループ化することです。この方法では、複数の静的/グローバルにアクセスするときに多くの異なるリテラルを格納するのではなく、1 つのリテラル (グローバル構造のアドレス) のみを格納し、そこからオフセットを計算します。

「シングルトン」クラスを、独自のインスタンス ポインターの管理から、大きな「struct GlobalTable」のメンバーになるように変換しました。これにより、コード サイズ (数パーセント) だけでなく、場合によってはパフォーマンスにも顕著な違いが生じます。

それ以外の場合:自明に構築されていないデータの静的構造と配列に注意してください。これらのそれぞれは、通常、これらの配列を適切に設定するために main() の前に実行される大量の .sinit コード (必要に応じて「非表示関数」) を生成します。統計で簡単なデータ型しか使用できない場合は、はるかに有利になります。

これもまた、「nm」や「objdump」などの結果に対してツールを使用することで簡単に特定できるものです。大量の .sinit がある場合は、調査する必要があります!

ああ、そして -- お使いのコンパイラ/リンカーがサポートしている場合は、特定のファイルまたは関数だけの最適化またはより小さな命令セットを選択的に有効にすることをためらわないでください!


重複コードのリファクタリングは、プログラムのメモリ フットプリントに最大の影響を与えるはずです。