2015-03-05 16 views
5

Sorunum oldukça basit, lambda'yı bir "karşılaştırıcı" olarak kullanabileceğim şekilde kullanmak istiyorum, biraz daha iyi açıklayayım. İki büyük yapılıma sahibim, her ikisi de operator< kendi uygulamasına sahipler ve ayrıca iki yapıyı kullanan bir useless sınıfına (bu sorunun bağlamında yalnızca sınıfın adı) sahipler, her şeye benziyor:Bir işlev nesnesi yerine lambda kullanmak, kötü performans

struct be_less 
{ 
    //A lot of stuff 
    int val; 
    be_less(int p_v):val(p_v){} 
    bool operator<(const be_less& p_other) const 
    { 
     return val < p_other.val; 
    } 
}; 

struct be_more 
{ 
    //A lot of stuff 
    int val; 
    be_more(int p_v):val(p_v){} 
    bool operator<(const be_more& p_other) const 
    { 
     return val > p_other.val; 
    } 
}; 

class useless 
{ 
    priority_queue<be_less> less_q; 
    priority_queue<be_more> more_q; 
public: 
    useless(const vector<int>& p_data) 
    { 
     for(auto elem:p_data) 
     { 
      less_q.emplace(elem); 
      more_q.emplace(elem); 
     } 
    } 
}; 
iki yapı yıllarda tekrarını kaldırmak ister whould

, simpliest fikri yapı bir şablon yapmak ve karşılaştırma işi yapmak için iki functor sağlamaktır: oldukça iyi

template<typename Comp> 
struct be_all 
{ 
    //Lot of stuff, better do not duplicate 
    int val; 
    be_all(int p_v):val{p_v}{} 
    bool operator<(const be_all<Comp>& p_other) const 
    { 
     return Comp()(val,p_other.val); 
    } 
}; 

class comp_less 
{ 
public: 
    bool operator()(int p_first, 
        int p_second) 
    { 
     return p_first < p_second; 
    } 
}; 

class comp_more 
{ 
public: 
    bool operator()(int p_first, 
        int p_second) 
    { 
     return p_first > p_second; 
    } 
}; 

typedef be_all<comp_less> all_less; 
typedef be_all<comp_more> all_more; 

class useless 
{ 
    priority_queue<all_less> less_q; 
    priority_queue<all_more> more_q; 
public: 
    useless(const vector<int>& p_data) 
    { 
     for(auto elem:p_data) 
     { 
      less_q.emplace(elem); 
      more_q.emplace(elem); 
     } 
    } 
}; 

Bu işi Şimdi, iki ek işlev nesnesinin fiyatına yapı kodunda herhangi bir çoğaltma bulunmadığından emin olun. Lütfen unutmayın ki, operator<'un uygulanmasını çok basitleştirdiğim için, hipotetik gerçek kod sadece iki virgülü karşılaştırmaktan çok daha fazlasını yapar. Sonra

i (sadece bir deneme olarak) lambda kullanarak i uygulamak başardı hayranlarıyla tek çalışan çözüm aynı şeyi yapmak için nasıl düşünüyordum geçerli:

template<typename Comp> 
struct be_all 
{ 
    int val; 
    function<bool(int,int)> Comparator; 
    be_all(Comp p_comp,int p_v): 
     Comparator(move(p_comp)), 
     val{p_v} 
    {} 
    bool operator<(const be_all& p_other) const 
    { 
     return Comparator(val, p_other.val); 
    } 
}; 

auto be_less = [](int p_first, 
      int p_second) 
{ 
    return p_first < p_second; 
}; 

auto be_more = [](int p_first, 
      int p_second) 
{ 
    return p_first > p_second; 
}; 

typedef be_all<decltype(be_less)> all_less; 
typedef be_all<decltype(be_more)> all_more; 

class useless 
{ 
    priority_queue<all_less> less_q; 
    priority_queue<all_more> more_q; 
public: 
    useless(const vector<int>& p_data) 
    { 
     for(auto elem:p_data) 
     { 
      less_q.emplace(be_less,elem); 
      more_q.emplace(be_more,elem); 
     } 
    } 
}; 

Bu uygulama sadece yeni üye eklemek Yapısını içeren veriye, ama aynı zamanda çok zayıf bir performansa sahip olduğum için, size burada gösterdiğim işe yaramaz sınıf için bir örnek oluşturduğum küçük bir test hazırladım, her zaman 2 kg'lık bir vektörle kurucuyu besliyorum Tamsayılar, sonuçlar şu şekildedir:

  1. E 48 ms alır
  2. ikinci yararsız sınıf oluşturmak için 228ms Alır ilk işe yaramaz sınıfı (funktor)
  3. üçüncü yararsız sınıf oluşturmak için 557ms Alır Açıkça (lambda'lar)

i kaldırıldı bedeli yapıcısı xecute çoğaltma çok yüksek ve orijinal kodda çoğaltma hala var. Unutmayın ki, üçüncü uygulamanın performansının ne kadar kötü olduğunu, orijinalin on kat daha yavaş olduğunu, üçüncü uygulamanın nedeninin ikinci saniyeden daha yavaş olduğuna inandığımı, be_all kurucusundaki ek parametreden kaynaklandığını ancak ...

template<typename Comp> 
struct be_all 
{ 
    int val; 
    be_all(int p_v):val{p_v} 
    {} 
    bool operator<(const be_all& p_other) const 
    { 
     return Comp(val, p_other.val); 
    } 
}; 

bool be_less = [](int p_first, 
      int p_second) 
{ 
    return p_first < p_second; 
}; 

bool be_more = [](int p_first, 
      int p_second) 
{ 
    return p_first > p_second; 
}; 

typedef be_all<decltype(be_less)> all_less; 
typedef be_all<decltype(be_more)> all_more; 

class useless 
{ 
    priority_queue<all_less> less_q; 
    priority_queue<all_more> more_q; 
public: 
    useless(const vector<int>& p_data) 
    { 
     for(auto elem:p_data) 
     { 
      less_q.emplace(elem); 
      more_q.emplace(elem); 
     } 
    } 
}; 

i auto kaldırırsanız:

Aslında da hala lambda kullanılan dördüncü durumda, var ama Comparator üyesinin ve be_all ek parametre kurtulmak, kod şudurkullanıyorum bile lambda ve bool yerine kod oluşturmak kullanın operator<'da.

  1. 48ms: bana çok garip Ne

    bu dördüncü uygulama (Comparator üyesi olmadan lambda) takip ediyor i kayıt başardı ortalama performans sonunda, diğerinden daha bile yavaş olmasıdır

  2. 228ms
  3. 557ms
  4. 698ms

Neden functor Bu senaryoda lambdalardan çok daha hızlı mı? Lambda'nın sıradan bir functor kadar iyi performans göstermesini bekliyordum, birisinin yorumunu yapabilir mi lütfen? Dördüncü uygulamanın üçüncü seviyeden daha yavaş olmasının teknik bir sebebi var mı?

PS:

i kullanıyorum compilator gr ++ -o3 ile 4.8.2 olduğunu.

namespace benchmark 
{ 
    template<typename T> 
    long run() 
    { 
     auto start=chrono::high_resolution_clock::now(); 
     T t(data::plenty_of_data); 
     auto stop=chrono::high_resolution_clock::now(); 
     return chrono::duration_cast<chrono::milliseconds>(stop-start).count(); 
    } 
} 

ve:

cout<<"Bad code: "<<benchmark::run<bad_code::useless>()<<"ms\n"; 
cout<<"Bad code2: "<<benchmark::run<bad_code2::useless>()<<"ms\n"; 
cout<<"Bad code3: "<<benchmark::run<bad_code3::useless>()<<"ms\n"; 
cout<<"Bad code4: "<<benchmark::run<bad_code4::useless>()<<"ms\n"; 

giriş tamsayılar kümesi, plenty_of_data herkes için aynıdır benim testte ben her useless sınıf için ben gerekli zamanı dikkate alınacak bir örnek ve kullanma kronostratigrafik oluşturmak 2 milyondan fazla tam bir vektördür. Zaman

+2

TL; DR: Karşılaştırmalı bir functor genellikle bir ya da daha fazla 'bool operatörüne() (T, U)' 'daha az' –

+2

'değerine sahip olduğumu değerlendirmektedir. Açıkça işaretlenmiş 4 ölçülü vakayı içeren bir kod bloğu yazdırabilir miyim, kendi derlememde tek bir kopyada yürütüp yapıştırabilir miyim? –

cevap

4

için

teşekkürler Bir lambda ve bir functor çalışma zamanını karşılaştırmıyorsanız. Bunun yerine, sayılar bir functor ve bir std::function kullanımındaki farkı gösterir. Ve std::function<R(Args...)>, örneğin, R(Args...) imzasını karşılayan herhangi bir Callable saklayabilir. Bunu tip silme ile yapar. Yani, gördüğünüz fark, std::function::operator() numaralı telefondan sanal aramanın yükünden geliyor.

Örneğin, libc++ uygulama (3.5), bir virtual operator() olan bir taban sınıfı template<class _Fp, class _Alloc, class _Rp, class ..._ArgTypes> __base sahiptir. std::function, bir __base<...>* depolar. std::function, F ile bir F ile oluşturduğunuzda, __base<...> türünden devralan ve sanal operator() geçersiz kılan bir nesne template<class F, class _Alloc, class R, class ...Args> class __func türünde bir nesne oluşturulur.

+0

Dördüncü uygulamanın std :: işlevini bir şekilde örtük olarak kullandığını mı düşünüyorsunuz? 'Comp' std :: işlevi çözüldü? Gördüğünüz gibi “be_all” herhangi bir std :: function bildirmez. – fjanisze

+2

Bu uygulamayla ilgili olarak şu sözüme başvuruyorum: "template struct be_all { int val; işlevi Karşılaştırıcı; 'içinde. Sorunun daha fazla parçası varsa, sorunun uzunluğunu göz önünde bulundurarak kaçırmış olabileceğimi itiraf ediyorum. – Pradhan

İlgili konular