2016-01-05 15 views
5

varsayalım Aşağıdaki kod var:derleyici Öner için seçici satır içi işlevi çağırır

struct Foo { 
    void helper() { ... } 
    void fast_path() { ...; helper(); ... } 
    void slow_path1() { ...; helper(); ... } 
    void slow_path2() { ...; helper(); ... } 
}; 

fast_path() performans kritik ve bu yüzden her (makul) çaba mümkün olduğunca hızlı yapmak yapılmalıdır olduğu yöntemi. slow_path1() ve slow_path2() yöntemleri performans açısından kritik değildir.

Anlayışımdan, tipik bir derleyici bu koda bakabilir ve helper(), birden çok yöntem işlevi arasında paylaşıldığı için toplam komut boyutunu azaltmak için yeterince karmaşıksa helper() satır içi satır aramamasına karar verebilir. Yavaş yol yöntemleri yoksa, aynı derleyici helper() satır içi olabilir.

Verilen bizim arzu performans özellikleri, biz derleyici fast_path() içine helper() çağrısını satır içi ama slow_path1() ve slow_path2() yılında derleyici'nın varsayılan davranışını tercih etmek istiyorum.

Bir geçici çözüm derleyici fast_path() paylaşılır helper() bir kullanım görür asla böylece, yavaş yol işlev tanımları ve ayrı derleme birimlerinde canlı fast_path() çağrısını sahip olmaktır. Ancak bu ayrımı korumak özel dikkat gerektirir ve derleyici aracılığıyla zorlanamaz. Ayrıca, dosyaların (Foo.h, FooINLINES.cpp ve şimdi de Foo.cpp) çoğalması arzu edilmez ve ek derleme birimleri belki de sadece bir başlık kütüphanesi olabilirdi.

Daha iyi bir yolu var mı?

İdeal Böyle kullanabilirsiniz yeni do_not_inline_function_calls_inside_me C++ anahtar sözcüğü isterdim: Alternatif

do_not_inline_function_calls_inside_me void slow_path1() { ... } 
    do_not_inline_function_calls_inside_me void slow_path2() { ... } 

, bir inline_function_calls_inside_me anahtar kelimeyi böyle: Bu varsayımsal anahtar süslemeleri olduğunu

inline_function_calls_inside_me void fast_path() { ... } 

Not *_path*() yöntemleri, helper() yöntemini değil.

Bu tür bir performans gereksinimine sahip olabileceğiniz örnek bir bağlamda, her katılımcının A ve B türlerinin genel global yayınlarını dinleyen bir uygulama yazdığı bir programlama yarışmasıdır. B tipi yayınlar alındığında, her uygulamada Daha önce yayınlanmış A tipi mesajların sırasına bağlı bir hesaplama yapın ve hesaplama sonucunu merkezi bir sunucuya gönderin. Her bir B tipi yayın için ilk doğru cevaplayıcı bir puan alır. Hesaplamalı problemin doğası, ön-hesaplamanın tip-A güncellemelerinde yapılmasına izin verebilir; Bunları hızlı bir şekilde yapmanın bir avantajı yok.

+0

Bir işlevin satır içine alınmasını engelleyen teknikler, derleyiciye özeldir. Tıpkı bir işlevi garanti eden tekniklerin belirtildiği gibi. Her neyse, öneriniz kusurlu ve "erken bir optimizasyon" zihniyetini gösterir. Bir fonksiyonun "performans açısından kritik" olduğu bir senaryo, pratikte, anlamsız olduğu kadar nadirdir. Programcılar, kodlarındaki performans noktalarını tanımladıklarını düşündüklerinden daha az yeteneklidirler - bu yüzden profilleyiciler gibi araçlar mevcuttur. – Peter

+0

@Peter Performans kritik kod yolları uygulamada nadir olabilir, ancak mevcutlar. Özellikle gcc ile ilgileniyorum. – dshin

+0

gcc ile '__attribute __ ((always_inline))' öğesini belirtebilirsiniz. –

cevap

3

Genel olarak, derleyiciden daha akıllı olmaya çalışmamalısınız. Modern derleyiciler, işlevleri nasıl işleyeceğine karar verirken harika bir iş çıkarır ve insanlar bunun hakkında akıl yürütmede kötü bir şekilde kötüdür. derleyici kendi tanımını görebilir ve uygun gördüğü şekilde bunları satır içi böylece Benim durumumda

, yapabileceğiniz en iyi aynı çeviri birimindeki inline işlevleri olarak orada tüm ilgili fonksiyonlara sahip olmaktır. Bununla birlikte, verilen bir fonksiyonun derleyici için geçerli olup olmadığına dair son kararı, belirli bir durumda faydalı bir etkiye sahip olduğuna dair kanıtınız olmadıkça, “zorla inline” kullanarak çok zorlu bir şekilde kullanın.

Derleyicinin işini kolaylaştırmak için, programınız hakkında ek bilgi sağlayabilirsiniz. GCC ve Clang'da bunun için function attributes'u kullanabilirsiniz.

struct Foo { 
    void helper(); 
    void fast_path() __attribute__ ((hot)); 
    void slow_path1() __attribute__ ((cold)); 
    void slow_path2() __attribute__ ((cold)); 
}; 

inline void Foo::helper()  { … } 
inline void Foo::fast_path() { … } 
inline void Foo::slow_path1() { … } 
inline void Foo::slow_path2() { … } 

Bu derleyici hızı ve küçük önbellek ayak izi için Foo::slow_path1 ve Foo::slow_path2 için daha agresif Foo::fast_path optimize etmek ipucu olacaktır. Bu işlevlerden herhangi biri Foo::helper numaralı telefonu çağırırsa, satır içi veya satır içi duruma göre karar verebilir. (Açıklamaların kesin etkisi için bağlantılı kılavuzdaki belgelere bakın.)

Derleyiciyi ima etmenin daha iyi bir yolu, gerçek profil verileri vermektir. GCC ile, programınızı -fprofile-generate seçeneği ile derleyebilirsiniz. Bu, ikili kodunuzu profil istatistiklerini toplayan bir kodla sağlayacaktır. Şimdi programınızı temsili bir dizi girdi ile çalıştırın. Bunu yapmak, toplanan profil verileriyle bir *.gcda dosyası oluşturacaktır. Şimdi -fprofile-use seçeneği ile yeniden derleyin. GCC, kodunuzdaki hangi yolların sıcak olduğuna ve birbirleriyle nasıl etkileşime girdiğine karar vermek için toplanan profil bilgilerini kullanır. Bu teknik profil güdümlü optimizasyon (PGO) olarak bilinir.

Elbette, böyle şeylerden endişe ediyorsanız, önce uygun optimizasyon seviyelerini (-O2) etkinleştirdiğinizden emin olun. Özellikle şablon ağır C + kodu (yani, standart kütüphane veya Boost'u kullanan neredeyse her şey), iyi bir optimizasyon olmadan derlendiğinde gerçekten çirkin bir makine kodu oluşturabilir. Ayrıca assert iyonlarını kodunuza (-DNDEBUG) derlemek isteyip istemediğinizi düşünün.

+0

Sıcak/soğuk öznitelikleri tam olarak istediğim gibi görünüyor. Onları hiç bilmiyordum - teşekkürler! – dshin

+0

Ancak, PGO önerinizle ilgili olarak: benim örneğimi gönderimimin son paragrafına bakacak olursanız, A tipi güncellemelerin B-tipi sürümlerden çok daha fazla sayılması durumunda bunun PGO için iyi bir aday olmadığını görürsünüz. – dshin

+1

Evet, bu zor olabilir. Belki de daha fazla * B * olayını tetikleyen profil çalışması için bir giriş dizisi hazırlayabilirsiniz. Fakat bu kaygan bir eğimdir. Profil verilerinin toplanması zorsa, manuel özelliklerle kalmak muhtemelen en iyi bahistir. – 5gon12eder