14336


例えば次のような場合を考えます。

[code]

    #include <unistd.h> // rand
    #include <vector> // std::vector
    #include <algorithm> // std::sort
    
    struct Foo {
      int key, value;
      Foo() { // 適当
        key = rand();
        value = rand();
      }
    };
    struct Comp {
      bool operator()(const Foo& a, const Foo& b) const {
        return a.value > b.value;
      }
    } comp;
    
    int main() {
      std::vector<Foo> a(100);
      std::sort(a.begin(), a.end(), comp);
      return 0;
    }
    
[/code]

上記の場合に、compを次のような関数に代えると

[code]

    inline bool comp (const Foo& a, const Foo& b) {
      return a.value > b.value;
    }
    
[/code]

の様に書き換えられます。functorで定義された関数はinline化の最適化がコンパイラで行われると聞きましたが、上記のように関数をinline宣言してしまえばfunctorとのパフォーマンスの差はなくなりますか？それとも他の最適化の要素があったりしますか？そもそもclassのmethodはinline化されるのがデフォですか？

functorがfunctionと比べてもっと柔軟なコーディングができるのはわかりますが、パフォーマンスにおいてはコンパイラのに詳しくないのでどのような最適化が行われ、得になるのかがわかりません。

P.S. もし本当に functor
にした場合のパフォーマンス上の恩恵が自動inline化だけであれば、簡単な関数をstructの定義を増やしながらfunctorを作る必要はないのではないでしょうか。ほとんどのstd::sortやstd::for_eachの例題は簡単な場合でもfunctorを作っており、一見、簡単な作業なのに、コードをややこしくしています。「簡単な作業ならinlineで定義した関数ポインター渡すだけでいいよ」と一言加えないのは、おそらく他の恩恵があるのではないか、と考えたからです。


CC BY-SA 3.0
2015-08-12T13:15:07.463
14333
2015-08-12T21:24:08.857
2015-08-12T14:03:27.373
10856
10856
question
c++ c++11
c++の functor を使う場合の function とのパフォーマンスの差はinline化だけですか？


> 関数をinline宣言してしまえばfunctorとのパフォーマンスの差はなくなりますか？

無くならないでしょう。

> そもそもclassのmethodはinline化されるのがデフォですか？

メンバ関数の実装本体をソースファイル（.cpp）に書くのではなくヘッダ（.h）に書いた場合は暗黙的に inline となります。

inline の理解が正しくないように思います。  
インライン展開されるかどうかは、定義した時点で決まるわけではありません。  
inline をつけたからといって必ずしもインライン展開されるとは限りません。  
通常、関数ポインタを経由して関数を呼び出す場合はポインタの指す関数が何なのかコンパイル時には不明なのでインライン展開はされないでしょう。

`std::for_each`
テンプレート関数に、関数ポインタを渡した場合と、関数オブジェクトを渡した場合でどのようにテンプレートが展開されるかを考えてみましょう。

[code]

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    struct Functor {
        void operator()(int &i) {}
    };
    void f(int &i) {}
    void g(int &i) {}
    
    int main() {
        int arr[] = {1, 2, 3, 4, 5};
        for_each(begin(arr), end(arr), &f);
        for_each(begin(arr), end(arr), &g);
        for_each(begin(arr), end(arr), Functor());
    }
    
[/code]

`std::for_each` の定義は以下の様になっているようなので、

[code]

    namespace std {
        template <class InputIterator, class Function>
        Function for_each(InputIterator first,
                          InputIterator last,
                          Function f);
    }
    
[/code]

関数ポインタを渡した場合は、

[code]

    void(*)(int&) for_each<int*, void(*)(int&)>(int*, int*, void(*)(int&));
    
[/code]

このように展開されるでしょう。

関数オブジェクトを渡した場合は、

[code]

    Functor for_each<int*, Functor>(int*, int*, Functor);
    
[/code]

このように展開されるでしょう。

関数ポインタを渡した場合だと、`for_each` からはそれが `void(*)(int&)`
形式の関数ポインタだということしか分かりません。上記の例のように全く同じ形式の別の関数 `f` と `g`
が渡されたりしたら、もう明らかにインライン展開できませんよね。

関数オブジェクトを渡した場合は `for_each` からもどのクラス（構造体）のインスタンスなのかがわかるので、呼び出される関数が `void
Functor::operator()(int &)` だということも明らかで、インライン展開されることが期待できます。

まぁでも、C++11 以降なら簡単なものはラムダ式を使うのがベストでしょう。記述も普通に関数を定義するより簡潔で、速度も関数オブジェクト同等のようです。


CC BY-SA 3.0
2015-08-12T17:52:55.843
14336
2015-08-12T17:52:55.843
2748
14333
answer


miteiさんは

> メンバ関数の実装本体をソースファイル（.cpp）に書くのではなくヘッダ（.h）に書いた場合は暗黙的に inline となります。

と書かれていますが正確ではなく、クラス定義内に関数宣言でなく関数の定義も含めた場合、暗黙的にinlineが指定されます。クラス定義自体はヘッダファイルに書かれることが多いから誤解されているようです。もちろんヘッダファイルであってもクラス定義の外に関数を定義した場合はinlineの対象にはなりません。

C++にはinline関数や上記のようにクラス内に記述された暗黙的なinlineメンバー関数などが多数あるため従来のようにコンパイル時にオブジェクトファイルを生成し、リンク時に単に結合するだけでは効率が悪くなってしまうため、大抵のコンパイラーはリンカーと協調し、リンク時コード生成という機能があります。  
これは関数のサイズや呼び出し関係から判断してインライン展開した方が効率の良さそうなものは自動的にインライン展開する機能です。当然ながらインライン化の判断基準にfunctorだからとかfunctionだからとかはありませんのでどちらもインライン展開される可能性はあります。


CC BY-SA 3.0
2015-08-12T21:24:08.857
14340
2015-08-12T21:24:08.857
4236
14333
answer