10

私の基本的な状況: のようなものを持つインクルード ファイルがある#define foo (Flag1 | Flags2 | Flag3)ため、ビット フラグの事前定義された組み合わせです。型の安全性のために、これらの #defines を static const static const int foo = (Flag1 | Flag2 | Flag3)(または同様のもの) に置き換えたいと考えました。このインクルード ファイルは、プログラム内の数十箇所に含まれています。

現在、関連するすべての最適化オプションを有効にしてリリース ビルドを行っているとき (VS2010 の C++ コンパイラを使用)、#defines を置き換えると、置き換えた定数の数に応じて、実行可能ファイルが数 KiB 増加するようです。

なぜこれが起こるのですか?static const私の知る限り、整数定数は、可能であれば生成される ASM コードに「インライン化」されるはずですが、 vsを使用#defineするとここでどのように違いが生じるかわかりません。逆アセンブリが示すように、明らかに変数はインライン化されていません。

#define:
01325041  or          eax,0FFD87FE0h
static int:
011E5451  or          eax,dword ptr [CMainFrame::s_TemplateModulePaths+38h (151F008h)]

最後の質問は次のとおり#defineです。生成されたアセンブリに変数が直接挿入されることを回避しながら、それでも依存するにはどうすればよいですか?

4

5 に答える 5

4

コンパイラが static const 変数を削除できなかった理由はありません。最適化をオンにしてコンパイルしている場合、VC++ がそれを行わないことに驚いています。

このコードを gcc でコンパイルしてみました。

enum { FLAG1 = 1 << 0, FLAG2 = 1 << 1, FLAG3 = 1 << 2 };

static const int foo = (FLAG1 | FLAG2 | FLAG3);

int main(){
    return foo;
}

最適化をオフにすると、値がインライン化されますが、変数用に記憶域が確保されます。

_main:
LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    call    ___main
    movl    $7, %eax  ;value inlined
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE0:
    .section .rdata,"dr"
    .align 4
__ZL3foo:                     ; storage space for foo
    .long   7

O2 では、値をインライン化し、ストレージ スペースを取り除きました。

_main:
LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
   .cfi_def_cfa_register 5
    andl    $-16, %esp
    call    ___main
    movl    $7, %eax    ; value inlined
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
   .cfi_endproc
LFE0:
; no storage space for foo

コンパイラの設定が正しくない、または VC++ がこの最適化に失敗しているということ以外の私の唯一の考えは、Flags 変数がコンパイル時の定数ではない可能性があるということです。これは、プログラムの起動時に式の値を計算する必要があることを意味します。インライン化を防ぎます。

于 2012-08-02T14:49:06.183 に答える
0

コメントに見られるように、型安全な演算子| 私の列挙型のオーバーロードは、VC++ が ORed 値をインライン化するのを妨げているようです。メリットがなければ実行可能ファイルのサイズを大きくするのは嫌なので、このバージョンを使い続けると思います#define(いいえ、これは時期尚早の最適化ではありません)。結局のところ、読みやすさは向上しません。フラグセットの列挙型も、型安全性を失うことはないと思います。

于 2012-08-03T12:20:47.543 に答える
-2

問題を別の視点から見ることで問題を解決できます。データとそれを使用する関数を近くに配置することができます。

OO 言語では、これは定数fooをクラスのプライベートな静的属性にすることを意味します。このクラスは、 を使用するすべての関数をパブリック メソッドとして持つ必要がありますfoo。静的なプライベート属性であるため、.cpp ファイルで定義され、実行可能なフットプリントにそのインスタンスが 1 つだけ存在します。

これはコードの大きな変更であり、単純なリファクタリングではないことを理解しています.fromの定義を静的グローバル定数に変更するほど単純ではないことは確かfooです#define。しかし、それらを使用するデータとメソッドを近くに (つまり、同じクラスに) 保持すると、多くの非機能的な利点がもたらされます。

于 2012-08-02T14:35:49.043 に答える
-2

私の推測ではstatic const int foo = (Flag1 | Flag2 | Flag3)、ヘッダーファイルにあると思います。これにより、定数のバージョンがすべてのオブジェクト ファイルに個別に保存されます。#define は、プーリングによってすべて結合されたリテラルを埋め込みます。

これを試してみてください。1 つの C++ ファイルに がありconst int foo = (Flag1 | Flag2 | Flag3)、次にヘッダーに がありextern const int fooます。


アップデート

私の答えがあなたのニーズに合わなかった場合は申し訳ありません。static const 変数のファイル サイズの問題に対処しました。

私はそれがすべてあなたが望むものだと思います。コードの一部がパフォーマンスの問題を引き起こしている場合を除き、この種のインライン最適化について心配することはありません。私にとって、型の安全性とファイル サイズは、CPU の最適化よりも重視するものです。さらに、extern const を使用すると、カバレッジおよびタイミング ツールによる分析が向上します。

時期尚早の最適化は、プログラミングにおけるすべての悪 (または少なくともその大部分) の根源です。

ドナルド・クヌース

于 2012-08-02T14:36:42.053 に答える