3 puan yazan GN⁺ 2024-03-18 | 1 yorum | WhatsApp'ta paylaş
  • Fedora ve Ubuntu 24.04 LTS, libc gibi OS bileşenlerini varsayılan olarak frame pointer'lar dahil edilerek derlenmiş hale geri döndürürken, Linux profiling ve Flame Graph'ların daha eksiksiz stack'ler görmesini sağlıyor
  • Frame pointer'lar yoksa profiler, libc gibi kütüphane katmanlarında stack walking işlemini durdurabilir veya yanlış frame'leri izleyebilir; bu da CPU ve Off-CPU Flame Graph'ların bozulmasına yol açabilir
  • gcc'nin 2004'te i386 için yaptığı değişiklik, bir register daha kazanma tercihiydi; ancak x86-64'e de yayılarak sistem profiler'ları ve eBPF tabanlı gözlemlenebilirlik için uzun vadeli bir maliyet bıraktı
  • Netflix'in Java ve libc uygulama deneyiminde maliyet genelde %1'in altındaydı; bazı raporlarda %1~2, özel microbenchmark'larda veya sıra dışı workload'larda ise %10'a kadar çıktı
  • LBR, DWARF, eBPF stack walker, ORC, SFrames, Shadow Stacks gibi alternatifler var; ancak bugün production'da performans iyileştirmeleri bulmanın en pratik yolu, frame pointer'ları varsayılan olarak etkinleştirmek

Flame Graph'lar neden bozulur?

  • CPU Flame Graph, görünüşte normal olsa bile libc'nin frame pointer'sız derlendiği sistemlerde bazı örnekleri yanlış biçimde [unknown] altında toplayabilir
    • Örnekte soldaki %15'lik örnekler yanlış konumdadır ve application frame'leri eksiktir
    • profiler, kernel frame'lerini geçip syscall ve libc syscall wrapper'a ulaştıktan sonra bir sonraki frame'in sembol çözümlemesinde başarısız olur
  • Bunun nedeni, derleyici optimizasyonuyla frame pointer register'ının stack frame için referans noktası olmak yerine veri depolamak için kullanılmasıdır
    • profiler bunun sıradan bir sayı mı olduğunu bilemez ve bunu function address olarak yorumlamaya çalışır
    • Değer bir sonraki frame'i göstermiyorsa stack walking durur
    • Tesadüfen geçerli bir pointer gibi görünürse yanlış junk frame oluşabilir
    • Değer kendisini gösteriyorsa perf'in azami frame sınırına ulaşana kadar bir junk frame yığını oluşabilir
  • Off-CPU Flame Graph, libc read/write ve mutex function'ları sık göründüğü için frame pointer olmadığında daha ciddi biçimde bozulabilir
  • Application'ın kendisi de frame pointer'sız derlendiyse, yalnızca kütüphaneler değil tüm stack trace de güvensiz hale gelir

Frame pointer'ların sağladığı bilgi

  • x86-64 ABI, %rbp'nin stack frame'in base pointer'ı, yani frame pointer olarak kullanılabileceğini tanımlar
  • Linux perf ve eBPF gibi harici profiler/debugger'lar bu bilgiyi kullanarak stack trace'leri yürür ve sonucu Flame Graph olarak görselleştirir
  • x86-64 ABI içinde %rbp kullanımının isteğe bağlı olduğuna dair bir dipnot bulunur
    • Stack frame'i %rsp ile indekslemek, prologue ve epilogue içinde iki komutu azaltabilir
    • %rbp ek bir genel amaçlı register olarak kullanılabilir

2004'te kaldırılması ve 20 yıllık etkisi

  • 2004'te gcc, i386 backend'de varsayılan olarak -fomit-frame-pointer -ffixed-ebp eşdeğeri davranışa geçecek şekilde değiştirildi
  • i386'da yalnızca 4 genel amaçlı register bulunduğundan, %ebp'yi serbest bırakmak kullanılabilir register sayısını ciddi biçimde artırıyordu
    • Değişiklik gerekçeleri arasında Intel'in icc derleyicisinden daha iyi performans elde etme isteği de vardı
    • O dönemde debugger'ların farklı stack walking tekniklerini desteklediği için bozulmayacağı düşünülüyordu
  • Bu değişiklik x86-64'e de uygulandı
    • x86-64'te 12'den fazla register bulunduğu için, bir register daha kazanmanın faydası i386'daki kadar büyük değildi
    • eBPF gibi günümüz sistem profiler'ları o zamanlar mevcut değildi ve bu değişiklik onların bazı durumlarda bozulmasına yol açıyor
  • Eric Schrock, 2004'te amd64'te 17. genel amaçlı register'ı kazanmanın, debug edilebilirlik kaybına değecek kadar büyük bir avantaj olmadığını düşünüyordu
    • /usr/bin'i frame pointer'sız derlemeye başlarsanız bunun kontrolden çıkacağını söylemişti
    • Linux'ta bu eğilim yalnızca /usr/bin ile sınırlı kalmayıp /usr/lib ve application code'a da yayıldı

Java, libc ve Netflix'teki uygulama deneyimi

  • 2014'te Netflix'e katıldığında, Java'nın frame pointer desteği eksik olduğu için application stack'lerinin tamamı bozuk durumdaydı
  • JVM c2 compiler için bir düzeltme geliştirildi ve Oracle bunu yeniden işleyerek JDK8u60'a -XX:+PreserveFramePointer seçeneği olarak ekledi
  • Java tarafındaki değişiklikle application code içinde birçok performans iyileştirmesi bulunabildi; ancak libc hâlâ bazı CPU örneklerini ve Off-CPU Flame Graph'ların çoğunu bozuyordu
  • Sonrasında production için frame pointer içeren özel bir libc derlendi ve Canonical ile birlikte Ubuntu için önceden derlenmiş libc üzerinde de çalışıldı
    • Bir süre libc6-prof kullanımı önerildi
    • libc6-prof, frame pointer içeren libc6 idi

Performans overhead'i ve istisna durumları

  • Production'a geçiş sürecinde libc ve Java'ya frame pointer eklemenin maliyeti genellikle %1'in altındaydı
  • İstisna olarak %10 overhead görülen bir application vardı
    • Groovy üzerinden 1000 frame'den uzun stack trace'ler üreten sıra dışı bir application'dı
    • Linux perf de bunu işleyemediği için Red Hat'ten Arnaldo Carvalho de Melo, Netflix workload'u için kernel.perf_event_max_stack sysctl'ini ekledi
    • Söz konusu ortam, düşük seviyeli donanım profiling özelliği olmayan bir sanal makineydi; bu yüzden %10'luk etkinin tamamının frame pointer'lardan kaynaklanıp kaynaklanmadığı doğrulanamadı
  • Diğer raporlarda maliyet yaklaşık %1 veya %2 düzeyindeydi
  • Microbenchmark'larda bozulma %10'a kadar çıkabiliyor
    • Küçük function'lar döngü içinde tekrar tekrar çalıştırıldığında, ek komutlar L1 cache sıcaklığını veya cache line sınırlarını etkileyebilir
    • Bu durumda aynı etkiyi frame pointer'dan bağımsız olarak hot function'a herhangi bir şey eklemek de yaratabilir
  • Python scimark_sparse_mat_mult benchmark'ı da %10'a ulaşabiliyordu
    • Andrii Nakryiko'nun analizine göre bu, büyük bir function içinde gcc'nin %rsp offset'i yerine %rbp relative offset kullanıp daha fazla byte gerektirmesi nedeniyle oluşan özel bir durumdu
    • Sonrasında Python'ın varsayılan olarak frame pointer'ları yeniden etkinleştirebilmesi için düzeltme yapıldığı da bildirildi
  • Frame pointer sayesinde bulunan performans iyileştirmeleri %5 ile %500 arasında değişiyordu; bu nedenle çoğu durumda %1'den düşük maliyetin makul olduğu düşünülüyor
  • Eğer bir cihazda profiling veya debugging imkânı gerekmiyorsa frame pointer'sız derleme yapılabilir; ancak asıl hedef enterprise Linux ve backend server'lar

Fedora ve Ubuntu'da varsayılan etkinleştirme

  • Meta, Google ve Netflix gibi büyük şirketler, profiling kabiliyeti elde etmek için zaten kendi frame pointer'lı libc sürümlerini kullanıyordu
  • Fedora'da bunu upstream'e taşıma yönündeki ilk girişim uzun bir tartışmaya dönüştü
    • Fedora tartışması 116 mesajlık bir başlığa dönüştü
    • Katılımcılardan biri, Meta veya Netflix'in test, benchmark ve code size ölçümleri için side repository altyapısı sağlaması gerektiğini savundu
    • Jonathan Corbet bunu Fedora's tempest in a stack frame yazısında özetledi
  • Fedora daha sonra öneriyi yeniden değerlendirip kabul etti ve frame pointer'ları yeniden etkinleştiren ilk dağıtım oldu
  • Ubuntu da Ubuntu 24.04 LTS'te varsayılan olarak frame pointer'lar duyurusunu yaptı
  • Arch Linux'un da frame pointer etkinleştirme sürecinde olduğuna dair bilgi eklendi
  • Bu değişiklik OS kütüphane stack walking'ini iyileştirir; ancak application runtime'ları için ayrıca ayar gerekebilir
    • Java, -XX:+PreserveFramePointer seçeneğini sunuyor
    • Go'da frame pointer desteği birkaç yıl önce varsayılan hale geldi

Frame pointer sonrası stack walking adayları

  • LBR(Last Branch Record): 16 veya 32 frame sınırına sahip Intel donanım özelliği; çoğu application stack'i için yetersiz olsa da, biraz stack bilgisi elde etmek için son çare olarak kullanılabilir
  • BTS(Branch Trace Store): Intel özelliği; stack derinliği sınırı daha az sorun olsa da bellek load/store ve BTS buffer overflow interrupt işleme maliyeti vardır
  • AET(Architectural Event Trace): JTAG tabanlı bir tracer; düşük seviyeli CPU, BIOS ve device event'lerini izleyebilir, ayrıca stack trace için de kullanılabilir gibi görünüyor, ancak doğrudan kullanım deneyimi yok
  • DWARF: debugger'larda uzun süredir kullanılan bir binary debuginfo yöntemidir
    • JIT runtime'larda JIT-to-DWARF çalışmaları olduğuna dikkat çekildi
    • Yoğun production server'larda c2'nin sürekli çalıştığı Java JVM'ler için pratik olması beklenmiyor
    • DWARF stack walking'in kendi maliyeti de yüksek
  • eBPF stack walking: harici tracer, runtime desteği olmadan JVM gibi runtime'ların içini yürüyebilir
    • Runtime içinden user space read ile çok fazla veri okumak gerekebileceği için overhead yüksek olabilir
    • Runtime değişikliklerine hassastır; bu yüzden language code base ile birlikte dağıtılıp sürdürülmesi daha uygundur
  • ORC(oops rewind capability): Linux kernel'in hafif stack unwinder'ı; yeni kernel'lerin frame pointer'ı kaldırırken stack walking'i koruyabilmesini sağlar
  • SFrames(Stack Frames): ORC tabanlı hafif bir user stack unwinding yöntemi
  • Shadow Stacks: Intel ve AMD'nin güvenlik özelliği; function return address'lerini ayrı bir donanım stack'ine push edip return anında doğrulayabilir ve stack trace için de kullanılabilir gibi görünüyor

Şu anda ne değişiyor?

  • 2004'te frame pointer'ları atlama gerekçeleri olan i386 performans kazancı, dönemin debugger uyumluluğu değerlendirmesi ve icc ile rekabet, 2024 koşullarına artık uymuyor
  • x86-64'e uygulanan frame pointer kaldırma kararı, o dönemde bile debug edilebilirlik kaybına göre yeterli avantaj sağlamadığı yönünde değerlendirilmişti
  • Fedora ve Ubuntu'nun frame pointer'ları geri getirmesiyle, 2024 sürümlerini kullananlar CPU Flame Graph'ları daha kolay anlayabilecek ve Off-CPU Flame Graph'lardan ilk kez gerçekten yararlanabilecek
  • Continuous profiler'lar da müşterilerden OS değişikliği istemeden daha eksiksiz profiler elde etmeyi kolaylaştıracak
  • Gelecekte SFrames veya Shadow Stacks gibi yöntemlerle yeniden frame pointer'sız stack trace mümkün olabilir; ancak bugün yapılabilecek en somut iyileştirme, frame pointer'ları varsayılan olarak açmak

1 yorum

 
GN⁺ 2024-03-18
Hacker News yorumları
  • 2000’lerin başında stack frame pointer’ı atlama uygulamasının yaygınlaşmaya başladığı zamanı hatırlıyorum
    O dönemde yoksul bir üçüncü dünya ülkesindeki bir üniversitede bilgisayar bilimi okuyordum ve eski, yavaş bilgisayarlar yüzünden ödevlerin çoğunda interpreter yerine compiler kullanıyorduk
    Düşük seviyeli veri yapıları, compiler’lar, assembly sayısal rutinleri, Minix aygıt sürücüleri gibi şeyleri implemente ettiğimiz çok ilginç dersler vardı ve bir program garip davranınca gdb’ye bağlanıp assembly seviyesinde doğrudan stack’i takip ederek debug ediyorduk
    Ama birden -fomit-frame-pointer moda olunca stack trace almak anlamsız hale gelmeye başladı ve segfault ya da illegal instruction debug etmek çok daha zorlaştı
    Sonunda bozuk debug oturumlarından kaçınmak için neredeyse her şeyde Python kullanmaya başladım; performansta bir iki basamak kaybettim ama Python öğrenmiş olmam sonradan işime yaradı

    • -fno-omit-frame-pointer seçeneğini bilmiyor muydun diye merak ettim
  • Fedora’nın anılmasına sevindim. Dağıtımın tamamında frame pointer’ı açık tutmak epey yorucu bir mücadeleydi
    Örn: https://pagure.io/fesco/issue/3084
    Frame pointer ek yükünün büyük olduğu efsanesi hâlâ sürüyor; bunun nedeni bir Python örneğinde +%10 yavaşlama görülmesiydi ve bu artık düzeltildi
    Gerçekte ölçülen ek yük %1’in altında ve belirli uygulamalarda elde edilen fayda çok daha büyük

    • “Gerçekte ölçülen ek yük %1’in altında” sözü abartılı görünüyor. Ek yük, ölçtüğünüz iş yüküne büyük ölçüde bağlıdır; dolayısıyla bunu genel geçer bir ifade olarak kullanmak zor
      Linux kernel tarafındaki ölçümlerle de pek uyuşmuyor; orada bunun %5–10 aralığında olduğunu görmüştüm: https://lore.kernel.org/lkml/20170602104048.jkkzssljsompjdwy...
      netperf, page allocator microbenchmark’ları, pgbench, sqlite gibi çeşitli iş yüklerinde frame pointer açıkken %5–10 ek yük görüldü ve PostgreSQL ile SQLite üzerinde etkisinin görülmüş olması önemli
      DBMS, sistemi ciddi biçimde zorlamanın iyi yollarından biridir
    • OCaml 5’te flame graph’ları düzgün kullanmak için ya frame pointer kullanmanız ya derinliği sınırlı olan ve CPU’ya bağlı olarak çalışmayabilen LBR kullanmanız ya da perf/eBPF tarafını OCaml’ın iki stack’ini işleyecek şekilde ciddi biçimde değiştirmeniz gerekiyor
      OCaml 5, OCaml kodu ile C kodu için ayrı stack’ler kullanıyor ve GDB bunları DWARF bilgisiyle birleştirebiliyor, ama perf’in DWARF call graph özelliği bunu yapamıyor: https://github.com/ocaml/ocaml/issues/12563#issuecomment-193...
      Gelecek sürümlerde de frame pointer’ı korumak için gerekçe lazımsa, OCaml 5 iyi bir örnek olabilir
      Fedora 39’da frame pointer’ın zaten varsayılan olarak açıldığını şimdi öğrenmiş oldum; ben ise günlük profiling işlerinde hâlâ çoğunlukla perf record --call-graph dwarf -F 47 -a kullanan, CentOS 7 benzeri sistemlerde çalışıyordum
    • 32 bit tarafta frame pointer hâlâ sorunlu; bu yüzden günümüzde IoT gibi alanlar için pek uygun değil
      Frame pointer’ın kaldırılmasının nedeni bir efsane değil, 64 bit öncesi dönemin gerçekleriydi ve bu da o kadar eski bir mesele sayılmaz
      Bugün bile eski 64 bit sistemlere yeni bir hayat vermeye çalışıyorsanız bu tür optimizasyonlar anlamlı olabilir
      İdeal olarak güvenlik açısından kritik sistemlerde de varsayılan olmalı, ama her şeyin “gözlemlenebilirlik” için optimize edilmesi gerekmiyor
    • Bu “%1’in altında” ek yüklerin birikerek bilgisayar kullanım hissini 30 yıl öncesine göre ölçülebilir biçimde yavaşlattığını düşünüyorum. Böyle küçük ek yükler sonuçta üst üste birikiyor
  • Apple’ın ARM tarafında doğru yaptığı şeylerden biri, x29 frame pointer’ın her zaman geçerli bir frame record’a işaret etmesini sağlaması
    leaf function ya da tail call gibi bazı fonksiyonlar listede bir kayıt oluşturmayabilir, ama bunun sonucu olarak debug bilgisi olmasa bile stack trace her zaman anlamlı kalıyor
    https://developer.apple.com/documentation/xcode/writing-arm6...

    • Apple platformlarında başka türden yorumlanabilirlik sorunları da sık yaşanıyor. Objective-C / Swift uygulamalarında derin biçimde iç içe geçmiş block ve closure’lar çok olduğundan, backtrace’in birden çok thread’e dağılması sık görülen bir durum ve bunun için hâlâ iyi bir çözüm bilmiyorum
  • 2005’te Google’da karşı taraftaydım ve o zamanki düşünce basitti
    $BIG_COMPANY her şeyi frame pointer ile derlemeye karar verse bile, topluluğun geri kalanı bunu yapmayacağı için çok daha büyük bir toplulukla kazanılamayacak bir tartışmayı sürdürmüş oluyorsunuz diye düşünüyordum
    Sonuçta bu tartışma neredeyse 20 yıllık bir tartışmaya dönüştü ve sonrasında gperftools içinde libunwind’i çalıştırmak için patch yazarken kendimi birkaç yıl boyunca libunwind’i maintain ederken buldum
    Şimdi başka bir bilişim alanına geçtim ve daha pasif bir gözlemciyim, ama tarihe karşı taraftan bakarak okumak ilginç geliyor

    • Frame pointer ekleyince insanın eli kolu nasıl bağlanıyor, bunu merak ediyorum. İşlevsel olarak neyin bozulduğunu bilmiyorum
  • Eğer RBP çerçeve işaretçisi olarak ayrılacaksa, iki ayrı stack kullanmak da mümkün
    Biri RBP tarafından işaret edilip etkin frame’leri tutar, diğeri RSP tarafından işaret edilip yalnızca dönüş adreslerini tutar
    Böylece çağrı stack’i kelimenin tam anlamıyla dönüş adreslerinden oluşan düz bir dizi olur ve artık “stack yürümek” gerekmez
    Zaten dönüş adreslerini yerel değişkenlerin yakınına kaydetmenin neden gerekli olduğunu bilmiyorum; çok fazla dezavantajı var gibi görünüyor

    • Bunun nedeni depolama yönetimini basitleştirmesi. Stack frame’leri önbellekte her zaman bulunan basit bir bump pointer yapısı ve taşma için yalnızca tek bir guard page gerekiyor
      Önerdiğiniz yaklaşımda iki guard page gerekir, stack işlemleri iki katına çıkar ve cache miss olasılığı da iki katına çıkar
    • Yazıda kısaca değinilen CPU özelliği shadow stack buna oldukça benziyor. Ancak asıl amacı güvenlik tarafı
    • Stack’in neden “yanlış yönde” büyüyüp, yanlış çalışan programların güvenlik sorunlarına yol açtığı da bana tuhaf geliyor
      Sebebini biliyorum ama birçok şey gibi bu da en son yaklaşık 30 yıl önce anlamlıydı ve etkileri ilginç oldu
    • Sanırım Forth’u sevmeye hazırım ;-)
      İlginç biçimde Wikipedia’daki yazı, Forth’un hem parametre stack’ine hem de dönüş stack’ine erişebilmesini modelin önemli bir özelliği olarak pek vurgulamıyor gibi görünüyor
      https://en.wikipedia.org/wiki/Forth_(programming_language)
    • Dönüş adreslerini başka yerde tutmanın faydası, donanım shadow stack benzeri bir şeyi desteklemedikçe net görünmüyor
      Ayrı bir sayfaya taşımak ve iki işaretçi yönetmek için ödenecek bedelin, zaten gereken noktalarda koruma sağlayan stack cookie/protector yaklaşımından gerçekten daha ucuz olduğunu göstermek gerekir
      Mevcut stack protector’lara göre de somut bir güvenlik avantajı yok. Rastgele okuma/yazma mümkünse, sonuçta bu yine kontrol akışı bütünlüğünü aşmaya kadar gidebilir
  • Virgil frame pointer kullanmıyor. Dinamik stack tahsisi yoksa, belirli bir fonksiyonun frame boyutu sabittir ve basit bir ikili arama tablosu sorgusuyla bulunabilir
    Virgil’in tekniği buna ek olarak sayfa indeksi tabanlı aralıklar kullanarak sorguyu ortalama birkaç karşılaştırmaya indiriyor; unwind bilgisini GC için stackmap ile birleştiriyor ve çok az yer kaplıyor
    Ana kod https://github.com/titzer/virgil/blob/master/rt/native/Nativ... adresinde, aynı dizindeki diğer kodlar ise metadata çözümlemeyi gerçekleştiriyor
    Frame pointer ancak frame boyutu dinamik olduğunda, yani stack üzerinde veri tahsis edildiğinde anlamlı
    Statik mekanizma yeterliyken dinamik mekanizma kullanmak tuhaf; bunun başlıca sebebi muhtemelen metadata kodlama ABI’si ya da unwind rutinleri üzerinde ortak bir uzlaşının olmaması
    %1–2 ölçümü makul görünüyor; dizi sınır denetimi maliyetiyle benzer düzeyde
    Hata ayıklama ve profilleme için %1 maliyete özel muamele yapıp güvenlik katmanı eklemeye direnmek, öncelikler açısından çok garip

    • C’ye de sınır denetimi eklenebilir ama maliyeti %1–2’den çok daha yüksek olur
      C++’ta std::vector varsayılan olarak sınır denetimini kapatır; bence bunun nedeni C++’ın tamamen çılgın insanlar tarafından ve onlar için tasarlanmış olması
      Bunun dışında sınır denetimi olmayan başka bir dil hemen aklıma gelmiyor
  • Güzel yazı. Frame pointer ortadan kaybolduğunda üzülmüştüm
    Linux dahil olmak üzere pek çok kişi, farklı sistemlerde frame pointer yokluğu yüzünden uzun süre sıkıntı çekti ve bunu olabildiğince çok ortamda korumaya çalıştı
    Ana akım Linux’un bunu geri getirdiğini görmek bir yandan doğrulanmış gibi hissettiriyor ama bir yandan da biraz sinir bozucu

    • Gerçekten merak ediyorum. DWARF ile stack unwind etmenin zahmetli olduğunu biliyorum ama bütün sistemdeki kodu yavaşlatacak kadar neden bu kadar kötü olduğunu anlamıyorum
      Debian türevlerinde yavaş olmasının nedeni, lisans gerekçeleriyle perf için yalnızca yavaş unwind yolunun paketlenmesi; araçlar düzgün olduğunda farkı neredeyse hiç hissetmiyorum
      Acaba benim gözden kaçırdığım şey ne?
  • Genel olarak frame pointer lehindeyim, ancak bu alanda birkaç yıl çalışınca fark ettiğim bazı noktalar var
    Frame pointer tabanlı stack unwind yöntemlerinin çoğu, DWARF unwind bilgisinde olmayan sorunları hesaba katmıyor. Frame kurulumu atomik değil; push $rbp ve mov $rsp $rbp olmak üzere iki komuttan oluşuyor ve snapshot push sırasında alınırsa üst çerçeve kaçırılabilir
    Kod incelenerek bunun etkisi azaltılabilir belki, ancak stack frame ile ilgisiz push %rbp kullanımları da olabileceğinden bu daha çok sezgisel bir yaklaşım gibi görünüyor
    Brendan’ın sözünü ettiği BPF tabanlı hızlı çekirdek içi unwind çözümünü de geliştirdim: https://web.archive.org/web/20231222054207/https://www.polar...
    Bu yaklaşım, DWARF CFI’yi doğrudan kullanmak yerine onu BPF içinde kullanılabilecek rastgele erişimli bir biçime dönüştürüyor
    Şu anda yalnızca frame pointer içeren JIT bölümlerini destekliyor, ancak JVM yorumlayıcı unwind’ini native unwind ile iç içe çalışacak şekilde uygulamanın mümkün olduğunu düşünüyorum
    İdeal olarak frame pointer etkinleştirme kullanım senaryosuna göre yapılmalı ve burada benchmarking kritik önemde
    Sektöre ve yazılımın niteliğine göre performans, gözlemlenebilirlik ve iş metrikleri arasındaki ödünleşim büyük ölçüde değişebilir
    Fedora tarafı burada son derece iyi ve titiz bir iş çıkarmış
    Ayrıca bağımlı kütüphaneler de dahil olmak üzere tüm sistem genelinde bu ayarı değiştirebilen bir build sistemi, yalnızca test için değil üretim ortamında kullanım için de çok faydalı
    Son olarak, Indu’nun üzerinde çalıştığı SFrame umut verici görünüyor. Kullanıcının frame pointer kullanıp kullanmamayı seçmesine imkân verirken mevcut sorunların çoğunu çözebilir gibi duruyor, ancak altyapının hazır olması ve herkesin yükseltme yapması birkaç yıl alabilir

    • İyi flame graph’lar elde etmek için Linux dağıtımının tamamında frame pointer’ları etkinleştirmek gerekiyor
      Gerçekte neler olduğunu anlamak için sistem genelinde analiz gerekli ve Fedora ya da Debian gibi bugünkü ikili Linux dağıtımı yapısında başka bir seçenek pratikte mümkün değil
    • Bu tek bir komutla da yapılabilir: ENTER N,0, yerel değişkenler için N kadar stack alanı ayırır ve kabaca PUSH EBP, MOV ESP,ESP, SUB SP,N ile aynıdır
      Yalnız ENTER komutunun x86-64’te olup olmadığını hatırlamıyorum
      Yine de CALL ile frame kurulumu arasında atomik değildir; snapshot CALL sonrasında ama ENTER öncesinde alınırsa frame kurulumu elde edilemez
      ENTER komutunun yaygın kullanılmamasının nedeni çok yavaş olduğunun düşünülmesidir
      LEAVE, yerine geçen komut dizisi kadar hızlı ya da daha hızlı olduğu için kullanılır, ama ENTER ikinci operand yüzünden performansı bozar
      Bu operand, iç içe fonksiyonların üst stack frame’lerine erişmesi içindir ve kullanım maliyeti çok yüksektir
    • Atomik olmayan frame kurulumu CPU profiler’ları için kesinlikle sorundur, ancak allocation profiling, Off-CPU profiling ve kesme tabanlı olmayan diğer profiling türleri için sorun değildir
    • JVM unwind tarafında da iyi ilerleme var
  • Profillerde görünen [unknown] dağlarının neden oluştuğunu nihayet anlamış oldum; ilginç
    Yine de bunu gerekçelendirmek kolay değil. %2 performans farkı aslında oldukça büyük bir fark
    Frame pointer eklenmesini daha ince ayrıntıyla kontrol etmek güzel olurdu
    Ayrıntılı profiling varsa, belirli fonksiyonların ya da derleme birimlerinin frame pointer’a ihtiyaç duyup duymadığı değerlendirilebilir
    Frame pointer eklenince dramatik biçimde yavaşlayan iş yüklerinin azınlıkta olduğu ve geri kalanının neredeyse etkilenmediği sonucuna şaşırmam

    • %2 performans farkı o kadar da büyük değil. Özellikle profiling ile darboğazları bulup %10’dan fazla iyileştirme elde edebiliyorsanız bu daha da geçerlidir
    • GCC’de bildirimlere attribute eklenerek fonksiyon bazında açıp kapatılabilir. Ancak LLVM’de çalışmıyor
      __attribute__((optimize("no-omit-frame-pointer")))
      __attribute__((optimize("omit-frame-pointer")))
    • Gerçek uygulamalarda performans maliyeti %2’den çok daha düşük olabilir
      Bu tür benchmark’lar biraz yapaydır; bu yüzden tamamen güvenmemek gerekir ve gerçek uygulamalarda sonuçlar çoğu zaman ciddi biçimde farklı olur
      Profiling önemlidir; kodu dikkatle profile ederek çeşitli bölümleri %20’ye kadar hızlandırdığım oldu
      Performans kaybına çok duyarlı bir uygulamanız varsa, laboratuvarda frame pointer’ları açıp profiling yapabilir, müşteriye dağıttığınız sürümde ise kapatabilirsiniz
    • Ölçülen overhead %1’in biraz altında. Geçmişte frame pointer yüzünden performansın ciddi biçimde düştüğü nadir örnekler vardı ama artık düzeltildi
    • Genellikle %2’den çok daha küçüktür
  • JIT kod desteği ne yazık ki iyi değil, ancak LLVM’de üretilen her metodun ve adresinin kaydedilmesini sağlayan mükemmel bir hook var
    Bu yüzden basit bir karma mod stack unwind çözümünü nispeten kolay kurabilirsiniz, ama çoğunlukla süreç içinde mümkündür
    Intel’in DNN tarafı bilgileri perf’in okuyabildiği ortak bir dosyaya döküyor gibi görünüyor, ancak oneDNN çekirdeklerinin kendisi RBP’yi sürekli yeniden kullandığı için pratikte pek işe yaramıyor
    Yazıdaki “Java JVM gibi JIT runtime’larda DWARF bilgisi yoktur” iddiası da şaşırtıcı
    Bunun varsayılan olarak kapalı mı olduğunu, yoksa gerçekten kullanılamaz durumda mı olduğunu merak ediyorum
    Aratınca konu genelde JVM stack’ine JNI/C tarafını da dahil etme isteğine çıkıyor: https://github.com/async-profiler/async-profiler/issues/215