TP: DR:
- Derleyici iç bileşenleri muhtemelen bu optimizasyonu kolayca arayacak şekilde ayarlanmamıştır ve büyük olasılıkla çağrılar arasındaki büyük işlevlerin içinde değil, yalnızca küçük işlevler için yararlıdır.
- Büyük işlevler oluşturmak için satır içi yapmak çoğu zaman daha iyi bir çözümdür
foo
RBX'i kaydetmez / geri yüklemezse , gecikme ve işlem hacmi dengesi olabilir .
Derleyiciler karmaşık makine parçalarıdır. Bir insan gibi "akıllı" değiller ve olası her optimizasyonu bulmak için pahalı algoritmalar genellikle ekstra derleme zamanında maliyete değmez.
Ben bu rapor GCC hata 69986 - itmek kullanarak -OS ile mümkün küçük kodu / / yeniden dökmek için pop 2016 yılında geri ; GCC geliştiricileri tarafından herhangi bir etkinlik veya yanıt gelmedi. : /
Biraz ilgili: GCC hatası 70408 - aynı arama korumalı kaydı yeniden kullanmak bazı durumlarda daha küçük kodlar verebilir - derleyici geliştiricileri bana GCC'nin bu optimizasyonu yapabilmesi için çok fazla iş gerektireceğini, çünkü değerlendirme sırasını gerektirdiğini söyledi foo(int)
hedeflemeyi daha basit hale getirecek şeye göre iki çağrı.
Eğer foo
kaydetmez / geri rbx
kendisi, ekstra bir mağaza / yeniden gecikme vs performansı (talimat sayımı) arasında bir tercih söz var x
> dönüş_değeri bağımlılık zincirinin -.
Derleyiciler genellikle gecikme işleminden fazla gecikmeyi tercih eder, örneğin imul reg, reg, 10
(3 döngü gecikme süresi, 1 / saat işlem hacmi) yerine 2x LEA kullanmak , çünkü kodların çoğu Skylake gibi tipik 4 geniş boru hatlarında saatte 4 uops / saatten önemli ölçüde daha azdır. (Daha fazla talimat / uops ROB'de daha fazla yer kaplıyor, ancak aynı sıra dışı pencerenin ne kadar ileride görebileceğini azaltıyor ve yürütme aslında muhtemelen 4'ten az uops / saat ortalaması.)
Eğer foo
yaptığı itme / Rbx pop, daha sonra gecikme için kazanmak için pek bir şey yok. Dönüş adresinde kodu getirmeyi geciktiren ret
bir ret
yanlış tahmin veya I-önbellek özledim olmadıkça , geri yüklemenin hemen sonra değil, hemen öncesinde gerçekleşmesi muhtemelen ilgili değildir .
Önemsiz işlevlerin çoğu RBX'i kaydeder / geri yükler, bu nedenle RBX'de bir değişken bırakmanın aslında çağrı boyunca gerçekten bir kayıtta kaldığı anlamına gelmez. (Hangi arama korumalı kayıt işlevlerinin seçildiğini rasgele seçmek bazen bunu hafifletmek için iyi bir fikir olabilir.)
Bu nedenle evet push rdi
/ bu durumda pop rax
daha verimli olur ve bu, arayanın kaydedilmesi / geri yüklenmesi için daha fazla talimat ve ekstra depolama / yeniden yükleme gecikmesi arasındaki dengeye ve dengeye bağlı olarak, küçük yaprak olmayan işlevler için kaçırılan bir optimizasyon olabilir .foo
x
rbx
Yığın çözme meta verilerinin, tıpkı bir yığın yuvasına sub rsp, 8
dökülmesi / yeniden yüklenmesi için kullanılmış gibi RSP'deki değişiklikleri temsil etmesi mümkündür x
. (Ama derleyiciler kullanmak yerine, ya bu optimizasyon bilmiyorum push
rezerv alanı ve bir değişkeni başlatabilir. Ne C / C ++ derleyicisi yerel değişkenler yaratarak yerine sadece? ESP kez artırma itme pop talimatları kullanabilirsiniz . Ve yapıyor bu aşkın için her yerel basmada .eh_frame
yığın işaretçisini ayrı ayrı hareket ettirdiğiniz için bir yerel değişken daha büyük yığın çözme meta verilerine yol açar . Bu da derleyicilerin çağrı korumalı regları kaydetmek / geri yüklemek için push / pop kullanmasını durdurmaz.
Bu optimizasyonu aramak için derleyicilere öğretmeye değer olursa IDK
Belki bir fonksiyon içindeki tek bir çağrıda değil, tüm fonksiyon hakkında iyi bir fikir olabilir. Dediğim gibi, foo
yine de RBX'i kurtaracak / geri yükleyecek kötümser varsayımlara dayanıyor . (Veya x'den dönüş değerine kadar olan gecikmenin önemli olmadığını biliyorsanız, işlem hacmi için optimizasyon yapmak önemlidir. Ancak derleyiciler bunu bilmez ve genellikle gecikme süresini optimize eder).
Bu kötümser varsayımı çok sayıda kodla yapmaya başlarsanız (işlevler içindeki tek işlev çağrıları gibi), RBX'in kaydedilmediği / geri yüklenmediği ve avantaj elde edebileceğiniz daha fazla durum almaya başlayacaksınız.
Ayrıca, bu ekstra kaydetme / geri yükleme push / pop'u bir döngüde istemezsiniz, sadece RBX'i döngü dışında kaydedin / geri yükleyin ve işlev çağrıları yapan döngülerde çağrı korumalı kayıtları kullanın. Döngüler olmadan bile, genellikle, çoğu işlev birden çok işlev çağrısı yapar. Bu optimizasyon fikri x
, ilk aramadan hemen önce ve sondan sonra aramalardan herhangi birini gerçekten kullanmazsanız geçerli olabilir , aksi takdirde call
bir aradan sonra bir pop yapıyorsanız , her biri için 16 bayt yığın hizalamasını koruma konusunda bir sorununuz varsa başka bir çağrıdan önce.
Derleyiciler genel olarak küçük fonksiyonlarda mükemmel değildir. Ancak CPU'lar için de harika değil. Derleyiciler callee'nin iç kısımlarını görüp normalden daha fazla varsayımlarda bulunmadıkça , satır içi olmayan işlev çağrılarının en iyi duruma getirme üzerinde en çok etkisi vardır . Satır içi olmayan bir işlev çağrısı örtük bir bellek engelidir: Arayan kişinin bir işlevin tüm dünyada erişilebilir verileri okuyabileceğini veya yazabileceğini varsayması gerekir, bu nedenle bu tür tüm değişkenlerin C soyut makinesiyle senkronize olması gerekir. (Kaçış analizi, adresleri işlevden kaçmadıysa, yerlilerin çağrılar boyunca kayıtlarda tutulmasına izin verir.) Ayrıca, derleyici, çağrı clobbered kayıtlarının tamamen engellenmiş olduğunu varsaymalıdır. Bu, arama korumalı XMM kayıtları olmayan x86-64 Sistem V'deki kayan noktayı emer.
Küçük fonksiyonlar bar()
arayanlara daha iyi gelir. -flto
Çoğu durumda dosya sınırlarında bile olabilmesi için derleyin . (İşlev işaretçileri ve paylaşılan kitaplık sınırları bunu yenebilir.)
Derleyicilerin bu optimizasyonları yapmak için uğraşmalarının bir nedeni , derleyici içlerinde , normal yığından farklı olarak bir sürü farklı kod gerektirmesi , arama korumalı nasıl kaydedileceğini bilen kayıt ayırma kodu gerektirmesidir. kaydeder ve kullanır.
yani, uygulanması gereken çok iş ve sürdürülmesi gereken çok kod olacaktır ve eğer bunu yapmak konusunda aşırı hevesli olursa, daha kötü kod oluşturabilir.
Ve ayrıca (umarım) önemli olmadığı; Bu konularda, sen inlining gerektiğini bar
, arayanın içine veya inlining foo
içine bar
. Bu, çok farklı bar
işlevler olmadıkça ve foo
büyük olmadığı ve bir nedenden dolayı arayanlara giremeyecekleri sürece sorun değildir.