3


1

特定のコードを最適化しないようにコンパイラーに指示する方法

たとえそのコードが到達可能でないとしても、あるコードを最適化しないように(私の場合はgに)コンパイラーに指示する方法はありますか? オブジェクトファイルにそれらのシンボルが欲しいだけです。

例:これは単純な関数です。たとえ呼び出されなくても、この関数をコンパイルしたいのです。

void foo(){
  Foo v;
}

正式なコンパイラ指令がない場合、それを重要な機能だとコンパイラに思わせるためのトリックはありますか? それとも少なくともそれを安全に無視することはできないと思わせる? 私はこのようなことを試しました:

extern bool bar;
void foo(){
  if(bar){
    Foo v;
  }
}

しかし、それはそれをしていないようでした。

(なぜ私が地球上でそれを望んでいるのかを本当に知りたいのであれば、https://stackoverflow.com/questions/555330/templates-use-forward-declarations-to-reduce-compile-time [this ]質問、ここでは、 `+ template class Foo `による明示的なテンプレートのインスタンス化の代わりに、単に ` Foo v +`を記述できるようにしたいだけです。最適化なしのデバッグモードで…​)

更新:

これが私がやりたいことです(コンパイル可能なミニの例として):

  • foo.h *(そのようなファイルは私に与えられ、変更できません)

template
struct Foo {
  T val_;
  Foo(T val) : val_(val) {
      // heavy code, long compile times
  }
};
  • foo-instantiation.cpp *

#include "foo.h"
void neverCalled() {
  Foo f(1);
}

// The standard way to instantiate it is this:
// template class Foo;
// but in reality it is often hard to find out
// exactly what types I have to declare.
// Usage like Foo f(1); will instantiate all
// dependent types if necessary.
  • foo-decl.h *(foo.hから抽出したインタフェース)

template
struct Foo {
  T val_;
  Foo(T val); // no heavy code, can include anywhere and compile fast
};
  • main.cpp *

#include
#include "foo-decl.h"

int main(int argc, char** argv){
  Foo foo(1);
  return 0;
}

コンパイル(最適化なし)

g++ -c main.cpp
g++ -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo

コンパイル(最適化)

g++ -O2 -c main.cpp
g++ -O2 -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo
main.o(.text+0x13): In function `main':
: undefined reference to `Foo::Foo(int)'
collect2: ld returned 1 exit status
  • 代わりにプリコンパイル済みヘッダーを試しましたが、テンプレートのインスタンス化 メソッドにより、コンパイルがはるかに高速になります。

  • 最適化せずに `+ foo-instantiation.cpp `をコンパイルすることはそうではありません ライブラリコード( ` foo.h +`など)の実行が遅くなるため理想的です。

7 Answer


7


あなたは一つの定義規則に遭遇しています。 1つのファイルに定義があります。

テンプレート構造体Foo {T val_; Foo(T val):val_(val){//重いコード、長いコンパイル時間}};

そして別の定義では:

テンプレート構造体Foo {T val_; Foo(T val); //重いコードはありません。どこにでも含めることができ、高速にコンパイルできます。

これはCでは明示的に許可されていません(同一の定義が1つだけ許可されています)。月(ただし、特定の重要な時期におけるコンパイラの内部状態)

基本的に、あなたはそのようなコードを書くことはできません - すみません。


4


  • extern *と宣言してもしなくても、*コンパイラ*は関数本体を最適化できません。これは、その関数が別のコンパイル単位から呼び出されていないことを認識できないためです。 * static *と宣言すれば最適化できますが、コンパイラが実際にこれを行っているとは思われません。

コンパイラは関数呼び出しを最適化することができます。

while(false){foo(); }

上記では、foo()の呼び出しを省略することができます。

OTOH、*リンカ*は、それらが呼び出されない場合、最後のexcecutableから関数本体を削除することができます。

上記およびその他の理由から、私たちはあなたの問題を診断するために本当に実際のコードを見る必要があります。


2


*#pragma *のトピックでドキュメントを検索してください。 この定義は、あらゆる種類のプロパティを指定できる一種のエスケープハッチです。 gccはサポートしているので、gもそうすることになるのは良いことです。 これらは移植性がない可能性が高いので注意してください。これはプロジェクトにとって問題になる場合もあれば、そうでない場合もあります。


2


コンパイラは決して使用されない変数を最適化しています。別のコンパイル単位から使用される可能性があるため、使用されないという理由で関数を最適化することはできません。 次のようなものと一緒に使用されている変数を、コンパイラに強制的に考慮させることができます。

void instantiation()
{
   Foo f;
   f; // mark the variable as if it is used.
}

// or:
Foo* instantiation()
{
   Foo *p = new Foo();
   return p; // The compiler cannot know if p will be used, it must compile
}

より良い解決策は、必要に応じてテンプレートを明示的にインスタンス化することです。

// .h
template
class Foo
{
public:
   Foo( T const & value );
   void set( T const & ); // whatever else
private:
   T value_;
};

// template implementation another file, not included from .h
// instantiation.cpp??
template
Foo::Foo( T const & value ) : value_(value) {}

template
void Foo::set( T const & v )
{
   value_ = value;
}

// explicit instantiation
template class Foo;
template class Foo;

// test.cpp
#include "header.h"
int main()
{
    Foo f(5);
    f.set( 7 );

    Foo f2; // linker error Foo() not defined
}

ユーザコードはヘッダを見てどのメソッドが存在するのかを知るだけで、実際の実装は知りません。 実装は、明示的なテンプレートのインスタンス化が行われる1つのコンパイル単位でコンパイルされます。

1つの型を明示的にインスタンス化するのを忘れた場合、それはコンパイルエラーではなくリンカエラーになります。

一つの定義規則

cの1つの定義規則では、各シンボルまたはクラスに定義できる定義は1つだけです。 通常のシンボルに対して複数の定義があることは簡単に検出できます(2つの `+ void f(){} +`を定義すると、リンカーは重複したシンボルを検出します)が、テンプレートでは少し注意が必要です。 テンプレートでは、通常ヘッダーファイルで宣言および定義されているため、注意が必要です。 コンパイラは使用されたシンボルを各コンパイル単位で生成し[1]、リンカは通常2つ以上の同等のシンボルを見つけます(std

vectorを持つ各コンパイル単位にstd :: vector :: push_back()がコンパイルされ、push_backを呼び出します)。

コンパイラはテンプレートコードに '弱い’シンボルとしてフラグを立てます。シンボルはここで定義されていますが、別のコンパイル単位でも定義でき、リンカはリンクエラーを発生させることなくシンボルを自由に破棄できます。 同じSTL機能を利用する異なるコンパイル単位を同じタイプでリンクする場合は、これが必要条件です。

gcc 4.2までは、gcc linuxリンカはそれ以上チェックせずに1つを除くすべての弱いシンボルを破棄します。 いくつかのリンカ(linuxのgccリンカは近い将来、4.2ではなく、4.3や4.4を知らなくてもよいでしょう)は、異なる 'weak’シンボルが実際に同じであることを確認し、エラー/警告を提供します。ユーザーに。

あなたのコードは、あなたが別の場所でテンプレートを再宣言しているという点で、ODRを破っています。 あなたは一度テンプレートを宣言し、上記のように外部でメソッドを実装する必要があります。 とにかく両方の定義に互換性がある場合(投稿したスニペットにあるように):すべてのメンバメソッドと属性はまったく同じで、同じ修飾子(virtual / const-ness …​)を持ちます。テンプレートの定義が1つだけです(繰り返しがあります)。

[1] Only those methods that are actually called in the code will be コンパイル済み:

template
struct Test
{
   void f() { std::cout << "f()" << std::endl; }
   void g() { std::cout << "g()" << std::endl; }
};
int main()
{
   Test t;
   t.f(); // compiler generates Test::f, but not Test::g
}


1


これは通常、コンパイラ指令によって行われます。 Cではそれは#pragmaになり、Delphi Pascalではそれは問題のコードの周りの\ {$ O-}、\ {$ O}です。 正確な構文と方法は実装に固有のものなので、使用しているシステムが何であれ、ドキュメントをチェックすることが問題です。

関数を離れて最適化しないことは非常に簡単ですが、1回か2回、特定のコードを最適化しないようにコンパイラーに指示する必要がある場合があります。 これは非常にまれであり、私が長い間遭遇したことではありませんが、時折起こることがあります。 それが行われるケースは、通常、後のcpuテクノロジの開発の前に構築された古いレガシーコードに対してコンパイルされているケースです - ハイパースレッディングはその好例です。


0


手元がわからない。 おそらく Precompiled headersはこの問題を解決するでしょうか。

これを少し変更するだけで、明らかにコード内で小さいテンプレートヘッダーを使用できるという問題には役立ちませんが、コンパイル時の問題に役立つ可能性があります(したがってテンプレートの必要性がなくなります)。


0


変数をvolatileとして宣言します。

揮発性Foo v。

通常は最適化を妨げます。 Intel C CompilerとMicrosoft Visual Studio 2008で確認しました。