2017-03-25 9 views
0

以下のコードは、テンプレート工場を示しています。 functor-classをとり、operator()の戻り値を返します。遅いテンプレート工場

以下のfunctor-classは、この場合は巨大な単純な構造体を返します。ints.resize(10000000,0)

私はこの工場を呼び出すと、私は大きな減速を受けます。特に、非常に頻繁に呼び出されるとfor (size_t i=0,j=10000; i!=j; ++i)のように。私は価値による復帰が疑わしい(たとえそれが基準によってのみ返っても)。

誰でもこの減速を解決する方法として私を啓発することはできますか?

#include <iostream> 
#include <chrono> 
#include <vector> 

template<typename T, typename... Args> 
auto generate_one(Args... args) -> decltype(T{args...}()) { 
    T t{args...}; // <<---- this is so slow!!! 
    return t(); 
} 

// template<typename T, typename... Args> 
// auto generate_one(Args... args) -> decltype(T{args...}()) { 
// auto start = std::chrono::system_clock::now(); 
// T t{args...}; 
// auto end = std::chrono::system_clock::now(); 
// auto elapsed = end - start; 
// std::cout << elapsed.count() << '\n'; 
// return t(); 
// } 

struct product_t 
{ 
    std::vector<int> ints; 
}; 

struct initial_product_t 
{ 
    product_t operator()() 
    { 
    product_t product; 

    product.ints.resize(10000000,0); 

    return product; 
    } 
}; 

struct updated_product_t 
{ 
    updated_product_t(product_t & product) 
    : product(product) 
    {} 

    product_t & operator()() 
    { 
    return product; 
    } 

    product_t & product; 
}; 

int main() 
{ 
    auto product = generate_one<initial_product_t>(); 

    for (size_t i=0,j=10000; i!=j; ++i) 
    { 
    std::cout << i << "/" << j << std::endl; 

    product = generate_one<updated_product_t>(product); 
    } 
} 

編集1

私は他のユーザーからの提案の後に次のコードを試してみました。しかし、いくつかのコンパイルエラーがあります。

struct updated_product_t 
{ 
    updated_product_t(product_t && product) 
    : product(product) 
    {} 

    product_t operator()() 
    { 
    return std::move(product); 
    } 

    product_t product; 
}; 

エラー

$ clang++ -std=c++14 stackoverflow.cpp -o test 
stackoverflow.cpp:60:15: error: no matching function for call to 'generate_one' 
    product = generate_one<updated_product_t>(product); 
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
stackoverflow.cpp:6:6: note: candidate template ignored: substitution failure 
     [with T = updated_product_t, Args = <product_t>]: no matching constructor 
     for initialization of 'updated_product_t' 
auto generate_one(Args... args) -> decltype(T{args...}()) { 
    ^         ~ 
1 error generated. 

障害がある場合、私は理解していません。

編集2

私が変更した - 左辺値と右辺値に深く掘った後 -

template<typename T, typename... Args> 
auto generate_one(Args&&... args) -> decltype(T{args...}()) { 
    T t{args...}; // ^^ 
    return t();  // a reference to a lvalue or rvalue 
} 

に工場そして、それは今std::moveで製品を返すように私はoperator()を変更しました。

struct updated_product_t 
{ 
    updated_product_t(product_t & product) 
    : product(product) 
    {} 

    product_t operator()() 
    { 
    return std::move(product); 
    } 

    product_t & product; 
}; 

これらの変更により、私は高速に実行されます。

+1

あなたがするベクトルを使用している正当な理由があります1行に40MBを10000回割り当てますか? –

+0

工場がないとどれくらい早いですか? –

+0

工場がなくても速いです。私は大きな減速をもたらすためにそれほど多くを配分し、何がうまくいかないかを見るのが簡単です。 – user1587451

答えて

0

遅さはここにある:

product = generate_one<updated_product_t>(product); 

あなたは毎回大きなベクトルの割り当てをやっています。ここではそれを修正する一つの方法です:

int main() 
{ 
    product_t product = generate_one<initial_product_t>(); 
    product_t* pp; 

    for (size_t i=0,j=10000; i!=j; ++i) 
    { 
     std::cout << i << "/" << j << std::endl; 

     pp = &generate_one<updated_product_t>(product); 
    } 
} 

それとも、ベクトルのデータをコピー避けるためにshared_ptrを保持するためにproduct_tをリファクタリングすることができます

struct product_t 
{ 
    std::shared_ptr<std::vector<int>> ints; 
}; 

struct initial_product_t 
{ 
    product_t operator()() 
    { 
     product_t product; 
     product.ints = std::make_shared<std::vector<int>>(10000000); 
     return product; 
    } 
}; 
+0

'pp'の代わりに' product'を再利用することはできますか? – user1587451

+0

std :: moveは余分な割り当てなしでここに助けてもらえますか? –

+0

@ArtYerkes確かに、 'updated_product_t'は' product_t && 'を取って' product_t'を返さなければなりません。 – Yakk