9

実行時にレポートを有効/無効にできるロギング モジュールを使用しています。通話は通常、次のようになります。

WARN(
     "Danger Will Robinson! There are "
     + boost::lexical_cast<string>(minutes)
     + " minutes of oxygen left!"
);

私は WARN にインライン関数を使用していますが、舞台裏でどの程度の最適化が行われているのか興味があります。プログラム全体での引数の評価にはコストがかかります。関数は次のWARNようになります。

bool WARNINGS_ENABLED = false;
inline void WARN(const string &message) {
    if (!WARNINGS_ENABLED) {
       return;
    }
    // ...
}

文字列引数の作成に副作用がない場合、コンパイラはそれを最適化しますか? 特定のレベルの最適化が必要-Oxですg++x?

4

5 に答える 5

12

実行時に警告を選択的に有効または無効にできるようにする必要がある場合、コンパイラーは呼び出しを最適化できません。

必要なのは、関数の名前をに変更しWARN2、次のようなマクロを追加することです。

#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)

これにより、警告を有効にしない限り、実行時のsの評価が防止されます。

do-whileは、コード内のどこでも使用できるようにするトリックです(nakedステートメント、braced if-block内のステートメント、unbraced if-block内のステートメント、bracedおよびunbraced whileステートメントなど)。

于 2008-11-12T00:52:16.700 に答える
6

-Sオプションを使用すると、GCC /G++の機能を確認できます。これにより、実際にアセンブルされる前にコードが出力されます。gcc(1)を参照してください。

この場合、GCCとG++はほぼ同じように動作します。そこで、最初にコードをCに変換して、さらにテストを行いました。

char WARNINGS_ENABLED = 0;

inline void WARN(const char* message) {
    if (!WARNINGS_ENABLED) {
        return;
    }
    puts(message);
}

int main() {
    WARN("foo");
    return 0;
}

gcc -O3 -S file.cを実行し、出力ファイル' file.s 'を調べます。GCCが何も削除しなかった
ことがわかります。

それはあなたが求めていたものではありませんが、コンパイラーにそのコードを最適化する機会を与えるために、WARNINGS_ENABLEDを定数にする必要があります。別の方法は、静的にし、そのファイル内の値を変更しないことです。ただし静的にすることには、シンボルがエクスポートされないという副作用があります。

static const char WARNINGS_ENABLED = 0;

inline void WARN(const char* message) {
  if (!WARNINGS_ENABLED) {
      return;
  }
  puts(message);
}

int main() {
    WARN("foo");
    return 0;
}

次に、GCCはコードを完全にクリーンアップします。

于 2008-11-12T00:37:37.493 に答える
1

いいえ、グローバル WARNING_ENABLED が const と宣言されていない限り、コンパイラはコードを最適化すべきではありません。

ところで、WARN がインライン関数である場合、無効になっていても、メッセージ構築の代償を支払うことになります (これは、文字列に lexical_cast と operator+ を使用した例では非常に非効率的です)。

関数とストリーム スタイルの両方のロギングをサポートする、いくつかの効率的な (ランタイムが無効な場合の最小限の (分岐予測 CPU でゼロに近い) オーバーヘッド)ロギング マクロを次に示します。

于 2008-11-12T01:35:51.963 に答える
1

副作用がないことを証明できる場合にのみ、最適化する機会があると思います(コンパイラが高価な関数呼び出しに対して行うのは難しいかもしれません)。

私はブーストの専門家ではありませんが、WARNINGS_ENABLED が true の場合にのみ評価されて文字列を生成するラムダを構築する方法があると思います。何かのようなもの...

inline void warnFunc(some_boost_lambda &message_generator) {
  if (WARNINGS_ENABLED) {
    cerr << message_generator() << endl;
  }
}

#define WARN(msg) warnFunc(...insert boost magic here to turn msg into a lambda...)
于 2008-11-12T00:21:56.713 に答える
0

プリプロセッサを使用してすべてを定義することはできませんか?

void inline void LogWarning(const string &message) 
{
  //Warning
}

#ifdef WARNINGS_ENABLED
#define WARN(a) LogWarning(a)
#else
#define WARN(a)
#endif

これは、ASSERT()マクロが機能する方法です。WARNの括弧内のすべてのコードは、プリプロセッサーを経由してコンパイラーに到達することすらありません。つまり、次のような他のことができるということです

#ifdef WARNINGS_ENABLED
// Extra setup for warning
#endif
//....
WARN(uses setup variables)

そして、それは両方の方法でコンパイルされます。

括弧内に副作用がないことをオプティマイザーに認識させることに関しては、どちらの方法でも証明するのが難しいかなり複雑なステートメント(つまり、高レベルの文字列操作)をそこに入れることができます。

于 2008-11-12T00:45:27.570 に答える