Lambdas neden derleyici tarafından düz işlevlerden daha iyi optimize edilebilir?


171

The C++ Standard Library (Second Edition)Nicolai Josuttis adlı kitabında lambdasların derleyici tarafından düz işlevlerden daha iyi optimize edilebileceğini belirtiyor.

Ayrıca, C ++ derleyicileri lambdaları sıradan işlevlerden daha iyi optimize eder. (Sayfa 213)

Neden?

Inlining söz konusu olduğunda artık bir fark olmaması gerektiğini düşündüm. Düşünebilmemin tek nedeni, derleyicilerin lambdalarla daha iyi bir yerel bağlamı olabileceğidir ve bu da daha fazla varsayım yapabilir ve daha fazla optimizasyon yapabilir.



Temel olarak, ifade sadece lambdalar için değil, tüm fonksiyon nesneleri için geçerlidir .
newacct

4
İşlev işaretçileri de işlev nesneleri olduğu için bu yanlış olur.
Johannes Schaub - litb

2
@litb: Ben buna katılmıyorum düşünüyorum. ^ W ^ W ^ W ^ W ^ W ^ W (standarda baktıktan sonra) Ben ortak parlance düşünüyorum (ve göre wikipedia), insanlar işlev nesnesi derken bazı çağrılabilir sınıf örneği anlamına gelir.
Sebastian Mach

1
Bazı derleyiciler lambdasları düz işlevlerden daha iyi optimize edebilir, ancak hepsi değil :-(
Cody Gray

Yanıtlar:


175

Bunun nedeni lambdasların fonksiyon nesneleri olmasıdır, bu yüzden onları bir fonksiyon şablonuna iletmek özellikle o nesne için yeni bir fonksiyon başlatır. Derleyici böylece önemsiz bir şekilde lambda çağrısını sıralayabilir.

Öte yandan, işlevler için eski uyarı uygulanır: bir işlev işaretçisi işlev şablonuna geçirilir ve derleyiciler geleneksel olarak işlev işaretçileri aracılığıyla çağrıları satır içine almakta çok fazla sorun yaşarlar. Bunlar olabilir teorik olarak satır içine yerleştirilmiş, ancak çevredeki fonksiyonu yanı inlined yalnızca.

Örnek olarak, aşağıdaki işlev şablonunu göz önünde bulundurun:

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

Böyle bir lambda ile çağırmak:

int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

Bu örneklemedeki sonuçlar (derleyici tarafından oluşturulur):

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}

… Derleyici _some_lambda_type::operator ()aramaları önemsiz bir şekilde bilir ve sıraya koyabilir. (Ve işlevi diğer herhangi bir lambda mapile çağırmak, her bir lambda farklı bir türe sahip olduğu için yeni bir örnek oluşturur .)map

Ancak bir işlev işaretçisi ile çağrıldığında, örnekleme aşağıdaki gibi görünür:

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

… Ve burada fher çağrı için farklı bir adrese işaret eder mapve böylece, derleyici belirli bir işleve çözümlenebilmesi için fçevreleyen çağrı mapda satır içine alınmadığı sürece derleyici çağrıları satır içi yapamaz f.


4
Belki de, aynı fonksiyon şablonunu farklı bir lambda ifadesiyle başlatmanın, benzersiz bir türle tamamen yeni bir işlev yaratacağını ve bunun bir dezavantaj olabileceğini belirtmek gerekir.
soğuk

2
@greggo Kesinlikle. Sorun, satır içi yapılamayan işlevler işlenirken (çok büyük oldukları için). Burada geri arama çağrısı, lambda durumunda hala satır içi olabilir , ancak bir işlev işaretçisi durumunda değil. std::sortBurada bir işlev işaretçisi yerine lambdas kullanarak bunun klasik bir örneği yedi kat (muhtemelen daha fazla, ama bu konuda veri yok!) getiriyor.
Konrad Rudolph

1
kimse bizim lambda geçiyoruz: Burada iki işlevi karıştırıyorsun @greggo için (örneğin std::sort, ya mapbizim örneğimizde) ve lamda kendisi. Lambda genellikle küçüktür. Diğer fonksiyon - illa ki değil. Biz aramaları inlining ilgilendiğinizden lambda diğer işlevi içinde.
Konrad Rudolph

2
@greggo biliyorum. Buna rağmen, cevabımın son cümlesi tam olarak bunu söylüyor.
Konrad Rudolph

1
Bu basit bir boole fonksiyonu verilmiş Ne (sadece tökezledi olan) meraklı bulmak olduğunu predkimin tanımı görülebilir ve gcc v5.3 kullanarak std::find_if(b, e, pred)satır içi değil pred, ama std::find_if(b, e, [](int x){return pred(x);})yok. Clang her ikisini de satır içine almayı başarır, ancak lambda ile g ++ kadar hızlı kod üretmez.
rici

26

Çünkü bir algoritmaya bir "işlev" ilettiğinizde, aslında işlev gösterecek bir işaretçiyi geçiriyorsunuzdur, böylece işaretçi aracılığıyla işleve dolaylı bir çağrı yapmak zorundadır. Bir lambda kullandığınızda, bir nesne içinde bu tür için özel olarak örneklenmiş bir şablon örneğine geçersiniz ve lambda işlevine yapılan çağrı, bir işlev işaretçisi aracılığıyla yapılan bir çağrı değil, doğrudan bir çağrıdır;


5
"Lambda fonksiyonuna çağrı doğrudan bir çağrıdır" - gerçekten. Aynı şey sadece lambdalar için değil, tüm fonksiyon nesneleri için geçerlidir . Sadece kolayca işaretlenemeyen fonksiyon işaretçileri, hiç değilse.
Pete Becker
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.