3 puan yazan GN⁺ 2025-03-11 | 1 yorum | WhatsApp'ta paylaş
  • CPython projesi yakın zamanda bayt kodu yorumlayıcısı için yeni bir uygulama stratejisi benimsedi. İlk sonuçlar, çeşitli platformlarda ortalama %10-15 performans artışı gösterdi
  • Ancak bu performans artışı esas olarak LLVM 19'daki bir regresyon sorununu aşmanın sonucuydu. Daha iyi karşılaştırma temelleriyle (ör. GCC, clang-18, belirli ayar bayraklarına sahip LLVM 19) kıyaslandığında performans artışı %1-5'e düştü

Performans sonuçları

  • Çeşitli derleyiciler ve yapılandırma seçenekleri kullanılarak CPython yorumlayıcısının birden çok derlemesi benchmark edildi. Testler Intel sunucusu ve Apple M1 Macbook Air üzerinde yapıldı.
  • Tüm derlemelerde LTO ve PGO kullanıldı. clang18 temel alınarak pypeformance/pyperf compare_to tarafından raporlanan ortalama kullanıldı.
  • Derleyici performans karşılaştırması
    • Apple M1 Macbook Air:
      • clang18: temel
      • clang19: 1.12 kat daha yavaş
      • clang19.taildup: 1.02 kat daha yavaş
      • clang19.tc: 1.00 kat daha yavaş
      • gcc: N/A
  • Kuyruk çağrısı yorumlayıcısı, clang-18 ile kıyaslandığında hâlâ hız artışı gösteriyordu, ancak clang-19'a geçildiğinde yaşanan yavaşlama daha çarpıcıydı.

LLVM regresyonu

Kısa arka plan

  • Geleneksel bayt kodu yorumlayıcıları, while döngüsü içindeki bir switch ifadesinden oluşur. Çoğu derleyici switch yapısını bir sıçrama tablosu olarak derler.
  • Modern C derleyicileri, etiketlerin adresini alıp bunu "computed goto" olarak kullanma kalıbını destekler. CPython, kuyruk çağrısı çalışmasına kadar bu kalıbı kullanıyordu.

LLVM 19 regresyonu

  • LLVM 19, tail-duplication geçişine sınırlamalar getirdi ve IR boyutu belirli bir eşiği aşarsa çoğaltmayı durdurdu. Bunun sonucu olarak CPython'da tüm dispatch sıçramaları birleştirildi ve computed goto tabanlı uygulamanın amacı tamamen boşa çıktı.

Ek tuhaflıklar

  • Kuyruk çağrısı çoğaltma mantığındaki değişikliğin regresyona yol açtığından emin olunuyor, ancak regresyonun boyutu tam olarak açıklanamıyor.
  • Modern işlemcilerde %2-4 hız artışı daha yaygın kabul ediliyor.

Computed goto gerekli mi?

  • clang19.nocg benchmark'ı, clang19'dan daha hızlı olduğunu iddia ediyor. Bu, derleyicinin switch tabanlı yorumlayıcı kullanarak aynı optimizasyonu yapabildiğini gösteriyor.

Düzeltme

  • LLVM pull request 114990 bu regresyonu düzeltti. Bu düzeltme beklenen performansı geri getiriyor.

Değerlendirmeler

Benchmarking üzerine

  • Sistem optimizasyonunda benchmark'lar ve benchmarking metodolojisi oluşturulur, ardından önerilen değişiklikler değerlendirilir.
  • Benchmark'lar, belirli veri noktalarını genellemek için daha fazla varsayım ve kabule ihtiyaç duyar.

Temel karşılaştırma çizgisi

  • Yeni bir çözüm veya yöntem önerildiğinde, bunu "şu anda bilinen en iyi yaklaşım" ile karşılaştırmak yaygındır.

Yazılım mühendisliği üzerine

  • Yazılım sistemleri karmaşık, birbirine bağlı ve hızla değişen yapılardır.
  • Optimize edici derleyiciler, programcının niyetine saygı gösterirken kodu da optimize etmek zorunda oldukları için bir gerilim içinde çalışır.

Optimize edici derleyiciler

  • musttail niteliği, optimizasyonla ilgili yeni bir derleyici özelliği türünü temsil eder. Bu, performansa duyarlı kod yazmak için daha güçlü bir stil sunabilir.

nix hakkında bir not daha

  • nix bu projede çok yararlı oldu. Birden çok Python yorumlayıcısı sürümünü yönetmek ve derlemek için büyük kolaylık sağladı.

1 yorum

 
GN⁺ 2025-03-11
Hacker News görüşleri
  • Merhaba. CPython'a tail-calling interpreter ekleyen PR'ın yazarıyım

    • Öncelikle, bu sorunun kökenini bulmak için neredeyse bir ay harcayan Nelson'a teşekkür etmek istiyorum
    • Ayrıca, böyle büyük bir hata yaptığım için çok utanıyor ve üzgün hissediyorum
    • Kullandığımız derleyicide böyle bir bug olabileceğini CPython ekibi de beklemiyordu
    • Özür blog yazısını buraya koydum: bağlantı
  • Benchmark yapmak gerçekten çok zor bir iş

    • Kısa süre önce bir algoritmayı yaklaşık %15 daha hızlı hale getirmenin bir yolunu buldum
    • Ancak test sırasında, daha hızlı sürümdeki fonksiyonu çağırmadan da orijinal kod %15 daha hızlı oldu
    • Bunun nedeni kod ve bellek yerleşimi meselesiydi; CPU cache ile hizalanma daha iyi hale gelmişti
    • Casey Muratori bu konuda ilginç bir seri yürütüyor
  • Yazarın bu sorunun gerçeğini ortaya çıkarmasını takdir ediyorum

    • Python 3.14'teki tail-call interpreter hâlâ iyi bir iyileştirme
    • Bu olay, benchmark sürecindeki titizliğin ve farklı ortamlarda test yapmanın önemini gösterdi
    • Ayrıca artık herkesin yararına olabilecek bir derleyici bug'ı bulunmuş oldu
    • "%X daha hızlı" sonuçlarının ne kadarının aslında benchmark artifact'lerinden ya da bilinmeyen regression'lardan kaynaklandığını merak ediyorum
  • Bu, C'nin "makineye yakın" bir dil olmadığının iyi bir örneği

    • clang-19, computed goto interpreter'ı "doğru" şekilde derliyor ama optimizasyon niyetinden tamamen farklı bir çıktı üretiyor
    • Diğer derleyici sürümleri de "naif" switch() tabanlı interpreter'a optimizasyon uyguluyor
  • Derleyicinin döngüleri düzenleme biçimini ayarlaması nedeniyle tail-call interpreter duyurulduğu kadar etkili değil

    • CPU mimarisi ve sürümü çok önemli
    • C abstract machine, niyeti düzgün ifade etmek için yeterince düşük seviyeli değil
    • Bazı paranoyak interpreter implementasyonları doğrudan assembly yazmaya geri dönüyor
    • luajit, verimli assembly döngüsü implementasyonlarını mimariler arasında taşınabilir kılmak için bir macro sistemi uyguluyor
  • Python build'lerinin performansını değerlendirmek çok zor

    • Kısa süre önce astral ekibi, conda-forge build'lerinin diğer build'lerin çoğundan daha hızlı olduğunu gösterdi
    • Tail-call interpreter'ın diğer build optimizasyonlarıyla birlikte nasıl çalıştığını merak ediyorum
  • İlgili tartışmalar:

  • Harika bir yazı

    • Atıf yapılan yazılardan birinde 3.14.0a5'in 3.13'ten 1.12 kat daha hızlı olduğu belirtiliyor
    • Benchmark'ın başka süreçlerle aşırı yük altındaki bir ortamda çalıştırılıp çalıştırılmadığı konusunda kafam karışık
    • Benchmark'lar, dış değişkenleri ortadan kaldırmak için sıkı şekilde kontrol edilen ortamlarda yürütülmeli
  • Kısa süre önce Python 3.9'dan 3.13'e kadar benchmark yaptım

    • 3.11'e kadar performans iyileşti, ancak 3.12 ve 3.13, 3.11'den yaklaşık %10 daha yavaştı
    • Kendi benchmark'ımın yeterli olmadığını düşündüm ama çekirdek servise dağıttığımda da aynı değişimi gözlemledim
  • Bu tür bir optimizasyonun tail-call optimization ile nasıl ilişkili olduğunu merak ediyorum

    • Interpreter jump table implementasyonu stack frame oluşturulmasını etkilememeli