8

別の質問のタイトルは次 のようになります:特定の翻訳単位で、コンパイラによって生成されたコンストラクターのコードをコンパイラーに明示的に生成させるにはどうすればよいですか?

私たちが直面している問題は、1 つのコード パスについて、1 つのオブジェクトの copy-ctor 呼び出しがインライン化されていない場合 (つまり、このコンストラクターが手動で実装されている場合) に、(徹底的に測定された) パフォーマンスが (約 5%) 向上することです。(コードのクリーンアップ中に、このクラスの余分な明示的に実装されたコピー ctor (17 メンバー) が削除されたため、これに気付きました。)

編集:生成されたアセンブリ コードチェックし、2 つの異なるコード バージョンについて説明したように、インライン化とコード生成が行われていることを確認したことに注意してください。

ここで、手動の copy-ctor コードを元に戻す (コンパイラが生成したものとまったく同じ) か、このクラスの copy-ctor をインライン化しない他の手段を見つけるかの選択に直面しています。

特定の翻訳単位でコンパイラが生成したクラス関数を明示的にインスタンス化する手段 (Microsoft Visual C++ の場合) はありますか? または、それらが使用される各翻訳単位で常にインライン化されますか? (状況をよりよく把握するために、gcc やその他のコンパイラに関するコメントも歓迎します。)


最初の2つの答えは誤解を示しているため、コンパイラによって生成されたクラス関数は、ユーザーによって宣言も定義もされていない場合、コンパイラ自体によってのみ生成されます。したがって、これらの関数はソースコードに存在しないため、修飾子を適用することはできません。

struct A {
  std::string member;
};

Aデフォルトおよびコピー ctor、dtor、およびコピー演算子があります。これらの関数はコードに存在しないため、一部の declspec を介して変更することはできません。

struct B {
  std::string member;
  B(B const& rhs);
};

B現在、ユーザー提供のコピー ctor があり、ユーザーはそれを実装する必要があります。コンパイラはそのコードを生成しません。


懐疑論者のためのもう少し背景:-) ...

このコードは MS Visual C++ を使用してコンパイルされていますが、組み込み (のような) (リアルタイム) システム用にリンクされています。パフォーマンスは、このシステムでタイミングを取ることによって測定されたので、タイミングを取った人はまともな数値を持っていると思います.

このテストは、2 つのコード バージョンを比較することによって実行されました。唯一の違いは、この 1 つのクラスのインラインと非インライン コピー ctor でした。インライン コードのタイミングは、約 5% 悪化しました。


さらに調べたところ、ある点で私が間違っていたことが明らかになりました。コンパイラ、複雑なコピー コンストラクターに対して個別の関数を生成します。これは独自の裁量で行われ、最適化設定にも依存します。したがって、私たちの場合、コンパイラは特定の状況で間違ったことをしています。これまでの回答から、コンパイラーに別の方法で伝えることができるようには見えません。:-(

4

6 に答える 6

5

$12.1/5-「暗黙的に宣言されたデフォルト コンストラクターは、そのクラスのインライン パブリック メンバーです。」.

ですから、私たちにできることはあまりありません。暗黙のコンストラクターはインラインでなければなりません。この点に関する他の動作は、おそらく拡張機能です

そうは言っても、

手動のコピー コンストラクター (コードのクリーンアップ中に削除したもの) が正しいことを行っていた可能性があります。例として、クラスのメンバーの 1 つ (17 個のうち) がポインター メンバーである場合、手動コピー コンストラクターがディープ コピーを処理した可能性があります (したがって、パフォーマンス ヒットが発生しました)。

したがって、手動のコピー コンストラクターを慎重に確認しない限り、それを削除して (潜在的にバグのある) 暗黙的なコピー コンストラクター (コンテキスト内) に依存することさえ考えないでください。

于 2010-10-06T08:16:40.930 に答える
3

インライン化がそれと関係があるとは思えません。コンパイラがコンパイラによって生成されたコピー ctor をインライン化する場合、明示的に定義されたものもインライン化しないのはなぜですか? (コンパイラの最適化ヒューリスティックが失敗して、インライン化されたコードが 5% 遅くなることも珍しいことです)

結論を急ぐ前に、

  • 生成されたアセンブリをチェックして、2 つのバージョンが実際にまったく同じことを行うことを確認します (同じ順序で、同じアセンブリを使用するなど。そうでなければ、パフォーマンスの違いの原因になる可能性があるため)。
  • コンパイラによって生成されたものが実際にインライン化されていること、および手動で定義されたものがインライン化されていないことを確認ください

その場合は、この情報で質問を更新していただけますか?

C++ では、コンパイラによって生成された関数をインライン化する必要があるかどうかを示す方法はありません。__declspec(noinline)関数のすべての責任をコンパイラに明示的に引き渡すため、 のようなベンダー固有の拡張機能でさえ役に立ちません。したがって、コンパイラは、それをどうするか、どのように実装するか、インライン化するかどうかを選択します。「この機能を実装してください」と同時に「機能の実装方法を制御させてください」とは言えません。関数を制御したい場合は、それを実装する必要があります。;)

C++0x では、可能かもしれません (これらのベンダー固有の拡張機能が として宣言された関数とどのように相互作用するかによって異なります= default)。

しかし、繰り返しますが、インライン化が問題であるとは確信していません。ほとんどの場合、2 つの関数によって異なるアセンブリ コードが生成されるだけです。

于 2010-10-06T09:27:00.003 に答える
0

多くの場合、問題があることがわかっているいくつかのコア タイプに分離するのが最善です。例 a:

class t_std_string {
    std::string d_string;
public:
    /* ... */

    /* defined explicitly, and out of line -- you know what to do here */
    t_std_string();
    t_std_string(const std::string& other);
    t_std_string(const t_std_string& other);
    ~t_std_string();

    inline std::string& get() { return this->d_string; }
    inline const std::string& get() const { return this->d_string; }
    /* ... */
};

struct B {
    t_std_string member;
    /* 16 more */
    /* ... */
};

または、一部を無料で利用できます。例 b:

/* B.hpp */

struct B {
private:

    /* class types */
    struct t_data {
        std::string member;

        /* 16 more ... */
    public:
        /* declare + implement the ctor B needs */

        /* since it is otherwise inaccessible, it will only hurt build times to make default ctor/dtor implicit (or by implementing them in the header, of course), so define these explicitly in the cpp file */
        t_data();
        ~t_data();

        /* allow implicit copy ctor and assign -- this could hurt your build times, however. it depends on the complexity/visibility of the implementation of the data and the number of TUs in which this interface is visible. since only one object needs this... it's wasteful in large systems */
    };
private:

    /* class data */
    t_data d_data;
public:
    /* you'll often want the next 4 out of line
       -- it depends on how this is created/copied/destroyed in the wild
     */
    B();
    B(const B& other);
    ~B();
    B& operator=(const B&);
};

/* B.cpp */

/* assuming these have been implemented properly for t_data */
B::B() : d_data() {
}

B::B(const B& other) : d_data(other) {
}

B::~B() {
}

B& B::operator=(const B&) {
    /* assuming the default behaviour is correct...*/
    this->d_data = other.d_data;
    return *this;
}
/* continue to B::t_data definitions */
于 2010-10-06T09:21:55.127 に答える
0

__declspec(noinline) .

ドキュメントには、メンバー関数にのみ適用されると書かれていますが、実際には無料の関数でも機能します。

于 2010-10-06T08:15:26.147 に答える
0

ある種のネストされたオブジェクトを使用できます。このようにして、ネストされたオブジェクトのコピー コンストラクターをメンテナンス フリーのデフォルトのままにしておくことができますが、noinline を宣言できる明示的に作成されたコピー コンストラクターがまだあります。

class some_object_wrapper {
    original_object obj;
    __declspec(noinline) some_object_wrapper(const some_object_wrapper& ref) 
        : obj(ref) {}
    // Other function accesses and such here
};

やむを得ない場合は、問題のクラスを .lib で個別にコンパイルし、それにリンクすることができます。それを別の翻訳単位に変更しても、VC++ によるインライン展開は停止しません。また、彼らが実際に同じことをしているのかどうかについても質問しなければなりません。デフォルトのコピー コンストラクターと同じことを行うのに、なぜ手動のコピー コンストラクターを実装したのですか?

于 2010-10-06T09:50:41.040 に答える
0

私自身の結論を追加し、詳細に入ることなく正確な質問に答えるには:

  1. コンパイラ、特に VC++ に、コンパイラが生成した ctor/dtor/etc をインライン化する、またはインライン化しないようにすることはできません。 --しかし

  2. オプティマイザは、その裁量で、コンパイラが生成した関数 (ctor) のコードをインライン化するか、このコードの「実際の」関数を生成するかを選択します。私の知る限り、この点に関してオプティマイザの決定に影響を与える方法はありません。

于 2010-11-05T07:53:23.330 に答える