Python 3.14 kuyruk çağrısı yorumlayıcısının performansı
(blog.nelhage.com)- CPython'ın yeni kuyruk çağrısı yorumlayıcısı ilk başta pyperformance ortalamasında %10~15 iyileşme sağlıyormuş gibi göründü, ancak referans çizgisi düzeltildiğinde gerçek kazanımın yapılandırmaya göre yaklaşık %1~5 aralığına indiği görüldü
- Büyük iyileşmenin, yeni uygulamanın kendi etkisinden çok LLVM 19 regresyonunu dolaylı olarak aşmasından kaynaklandığı anlaşıldı; asıl belirleyici etken, Clang 19'un mevcut computed goto dispatch yapısını doğru biçimde kopyalayamamasıydı
- Intel Raptor Lake i5-13500 üzerinde clang19 derlemesi clang18'den 1,09 kat daha yavaştı ve clang19.tc 1,03 kat daha hızlıydı; ancak Apple M1 MacBook Air'de clang19 1,12 kat daha yavaştı ve clang19.tc de 1,00 kat daha yavaş seviyesinde kaldı
- LLVM'nin tail duplication sınırı nedeniyle dolaylı atlama sayısı clang18'deki 332'den clang19'da 3'e düştü ve böylece computed goto tabanlı yorumlayıcının amaçlanan yapısı fiilen ortadan kalktı
- Kuyruk çağrısı yaklaşımı hâlâ anlamlı bir iyileştirme sunuyor, ancak
musttailgibi derleyici optimizasyonunun uygulanmasını açıkça zorunlu kılan bir yaklaşım performansa duyarlı kod için daha sağlam olabilir
Performans iyileştirmesi gibi görünen referans çizgisi etkisi
- CPython projesi yaklaşık bir ay önce bayt kodu yorumlayıcısı için yeni bir uygulama stratejisini birleştirdi
- İlk sonuçlar, çeşitli platformlarda ve pyperformance kıyaslamalarında ortalama %10~15 performans artışı gösterdi
- Sonraki analizler, bu büyük artışın esas olarak LLVM 19 regresyonunun tesadüfen aşılmasından kaynaklandığını ortaya koydu
- GCC, clang-18 ya da belirli ayar bayrakları uygulanmış LLVM 19 ile karşılaştırıldığında artış yaklaşık %1~5 düzeyine düşüyor
- Kuyruk çağrısı yorumlayıcısının kendisi gerçekten hız kazancı sağlıyor, ancak iyileşme ilk sayıların ima ettiğinden daha sınırlı
- Eğer clang-19 veya sonrası ile derleme yaptıysanız, mevcut yol gerçekte %10~15 daha yavaş olmuş olabilir
- Simon Willison,
python-build-standalonederlemesi ile Python 3.13 karşılaştırmasında %10 hız artışını yeniden üretti
- Simon Willison,
Benchmark yapısı ve temel sayılar
- Birden fazla CPython derlemesi Intel sunucuda ve Apple M1 MacBook Air üzerinde karşılaştırıldı
- Intel sunucu, Hetzner tarafından işletilen Raptor Lake i5-13500 idi
- Tüm derlemelerde LTO ve PGO kullanıldı
- Derleme yeniden üretimi için nix yapılandırması kullanıldı
- Karşılaştırılan sürümler şunlardı
clang18: Clang 18.1.8, computed gotogcc: GCC 14.2.1, computed goto, yalnızca Intel içinclang19: Clang 19.1.7, computed gotoclang19.tc: Clang 19.1.7, yeni kuyruk çağrısı yorumlayıcısıclang19.taildup: Clang 19.1.7, computed goto ve regresyonu aşmak için-mllvmayar bayrağı
clang18referans alındığında pyperformance ortalama sonuçları şöyleydi- Raptor Lake i5-13500:
clang19: 1,09 kat daha yavaşclang19.taildup: 1,01 kat daha hızlıclang19.tc: 1,03 kat daha hızlıgcc: 1,02 kat daha hızlı
- Apple M1 MacBook Air:
clang19: 1,12 kat daha yavaşclang19.taildup: 1,02 kat daha yavaşclang19.tc: 1,00 kat daha yavaş
- Raptor Lake i5-13500:
- Kuyruk çağrısı yorumlayıcısı, clang-18'e göre bir miktar hız artışı gösterdi, ancak clang-19'a geçişte ortaya çıkan performans kaybından daha küçük kaldı
clang18.tcölçülemedi- Çünkü kuyruk çağrısı yorumlayıcısı Clang 19'a yeni eklenen bir derleyici özelliğine bağlı
- Bu kısıt nedeniyle durumu anlamak için daha fazla benchmark kombinasyonu gerekti
LLVM 19 regresyonunun yarattığı dispatch çöküşü
- Geleneksel bir bayt kodu yorumlayıcısı, opcode'ları
whiledöngüsü içindekiswitchifadesiyle işler- Derleyici genellikle
switchyapısını bir jump table ve dolaylı atlama olarak derler
- Derleyici genellikle
- Uzun zamandır, her opcode gövdesine dispatch mantığını kopyalamanın bu tür yorumlayıcıları hızlandırabildiği biliniyor
- Yani opcode sonunda döngünün başına dönmek yerine, bir sonraki komutu çözümleme ve jump table indeksleme mantığı her opcode için ayrı yer alır
- C derleyicileri, etiket adreslerini alıp bunları computed goto olarak kullanma özelliği sunar ve CPython da kuyruk çağrısı çalışmasından önce bu yönteme dayalı bir yorumlayıcı döngüsü kullanıyordu
- Clang/LLVM, derleyici performansı gerekçesiyle computed goto içindeki çok sayıdaki
gotoyu dahili olarak tek bir indirectbr LLVM instruction içinde birleştirir- Ardından kod üretim aşamasında tail duplication yaparak dallanma mantığını her konuma yeniden kopyalar
- Bu akış, LLVM'nin eski bir blog yazısında üst düzeyde belgelenmiştir
- LLVM 19, belirli durumlarda ciddi derleme süresi veya bellek kullanım artışını önlemek için tail duplication pass sınırı getirdi
- CPython'da bu sınır, Clang'in dispatch atlamalarını birleştirilmiş halde bırakmasına neden oldu
- Sonuç olarak computed goto tabanlı uygulamanın amacı fiilen boşa çıktı
- Bu sorun, benzer yorumlayıcı döngülerine sahip başka dil uygulamalarında daha önce tespit edilmişti, ancak CPython'ı etkilediği bilinmiyordu
- Nesne kodu disassemble edilip dolaylı atlama sayısı sayıldığında fark doğrudan görülüyor
- clang18 derlemesindeki
_PyEval_EvalFrameDefault:jmp *332 adet - clang19 derlemesindeki
_PyEval_EvalFrameDefault:jmp *3 adet
- clang18 derlemesindeki
computed goto'nun belirsiz konumu
- Tail duplication mantığındaki değişimin regresyona yol açtığı, düzeltme sonrası clang-18 düzeyi performansın geri gelmesiyle doğrulandı
- Ancak regresyonun büyüklüğü tamamen açıklanmış değil
- Tarihsel olarak opcode dispatch kopyalamasının yorumlayıcıyı %20 ila %100 hızlandırdığı aktarılmıştı
- Modern işlemcilerde gelişmiş dallanma kestiricileriyle daha yeni araştırmalar yaklaşık %2~4 gibi daha küçük bir artış gösteriyor
- Python, yapılandırma seçeneği olarak tek bir
switchifadesi kullanan eski tip yorumlayıcıyı da destekliyorclang18.nocg: clang18'e göre 1,01 kat daha hızlıclang19.nocg: clang18'e göre 1,02 kat daha yavaşclang19: clang18'e göre 1,09 kat daha yavaş
clang19.nocgsürümününclang19'dan daha hızlı çıkması ek bir ters köşe oldu- Clang 18 veya uygun bayraklarla ayarlanmış Clang 19,
switchtabanlı yorumlayıcıda da dispatch mantığını her opcode gövdesine kopyalıyor
- Clang 18 veya uygun bayraklarla ayarlanmış Clang 19,
- Dolaylı atlama sayılarının karşılaştırması da bu farkı gösteriyor
clang18: 332clang18.nocg: 306clang19.nocg: 3clang19: 3
- Modern Clang ile computed goto yorumlayıcısının tamamı gereksiz karmaşıklık olabilir
- Çünkü derleyici aynı dönüşümü
switchtabanlı kodda da yapabiliyor - Tersine, computed goto'nun kendisi de bu dönüşümü garanti etmeye yetmedi
- Çünkü derleyici aynı dönüşümü
- GCC 14.2.1,
switchyapısını kopyalamadı ama computed goto kullanıldığında amaçlanan davranışı gerçekleştirdi
Düzeltme ve geçici çözüm yöntemleri
- LLVM pull request 114990, yazı yayımlandıktan hemen sonra birleştirildi ve regresyonu düzeltti
- Birleştirilmeden önceki benchmark'larda da bu düzeltmenin beklenen performansı geri getirdiği doğrulandı
- Düzeltme öncesi sürümlerde, regresyona yol açan PR'ın eklediği ayar seçeneğiyle tail duplication durma eşiği değiştirilebiliyor
- clang-19'da bu sınır çok büyük bir değere ayarlanırsa benzer davranış geri kazanılabiliyor
- LTO derlemelerinde bu seçeneği iletmek karmaşık
- Tail duplication, derleme sırasında değil kod üretimi sırasında gerçekleşir ve LTO derlemelerinde kod üretimi derleme zamanında değil link zamanında olur
- Bu nedenle bayrağın yalnızca derleyiciye değil
lld'ye de verilmesi gerekir
- Kullanılan yapılandırma örneğinde
./configureaşamasındaOPTveLDFLAGSiçine-mllvm -tail-dup-pred-size=5000ekleniyordu
Benchmark'ın ortaya çıkardığı referans çizgisi sorunu
- Benchmark'lar belirli derlemeler arasındaki performans farkını doğru ölçebilir, ancak bu sonucu “genel performans artışı” olarak genellemek ek varsayımlar gerektirir
- Kuyruk çağrısı yorumlayıcısı benchmark'ları, mevcut computed goto yorumlayıcısına göre %10~15 daha hızlı sonuç verdi, ancak bunu daha geniş bir sonuca genellemek için referans çizgisi karmaşıktı
- Performans çalışmalarında neyin referans olarak alındığı, tekrar tekrar zorlayıcı bir sorundur
- Kuramsal olarak bilinen en iyi yaklaşımı anlamak başka, gerçek işletim sistemi, derleyici seçenekleri ve bayrakları doğru ayarlamak başka bir meseledir
- Eski donanımda ya da yeniden üretmesi zor ölçekte yapılmış kamuya açık benchmark'lar doğrudan karşılaştırma için uygun olmayabilir
- Makine öğrenimi makalelerinde de algoritma iyileştirmesi iddia edildiğinde, çoğu zaman ilk önemli soru “ne yapıldı?” değil “hangi referansla karşılaştırıldı?” olur
- Kötü ayarlanmış bir referans çizgisiyle karşılaştırıldığında etkileyici sonuçlar elde etmek kolay olabilir
Optimize edici derleyiciler ve musttail
- computed goto örneği, optimize edici derleyicilerden beklenen şeylerin birbiriyle çatışabileceğini gösteriyor
- Derleyici programcının niyetine saygı duymalı ve aynı davranışı korumalıdır
- Aynı zamanda kodu hızlandırmak için karmaşık ve sezgisel olmayan dönüşümler de uygulayabilmelidir
- clang-19, computed goto yorumlayıcısını program davranışı açısından doğru derledi, ancak optimizasyon niyetiyle tamamen farklı bir çıktı üretti
- Clang'in diğer sürümleri, basit
switch()tabanlı yorumlayıcıya bile amaçlananla aynı optimizasyonu uyguladı - Kaynak kodu düzeyindeki computed goto ile makine kodu düzeyindeki dispatch kopyalaması neredeyse birbirinden bağımsız kavramlar gibi görünüyor
- Çalışma sonucu aynı olduğundan, mevcut araçlar bu farkı tutarlı biçimde ifade etmekte zorlanıyor
- Kuyruk çağrısı yorumlayıcısı musttail attribute temellidir
musttail, geleneksel anlamda gözlemlenebilir program davranışını değiştirmez, ancak daha çok optimizasyon aracıyla bir diyalog gibidir- Derleyicinin belirli bir optimizasyonu uygulayabilmesi gerekir ve bu optimizasyon gerçekleşmezse derlemenin başarısız olması istenir
- Bu yaklaşım, derleyiciler evrilirken bile performansa duyarlı kodu daha sağlam yazmanın bir tarzı olabilir
- Yorumlayıcının
whiledöngüsündeki computed goto'yu varsayımsal bir[[clang::musttailduplicate]]benzeri öznitelikle değiştirmek de değerlendirilebilir
nix'in sağladığı yeniden üretilebilirlik ve sınırlamalar
nix, çok sayıda Python yorumlayıcı derlemesini yönetmede büyük fayda sağladı- Deney sürecinde
gcc,clang-18,clang-19,clang-20olmak üzere dört derleyici ve çeşitli bayrak kombinasyonlarıyla onlarca Python yorumlayıcısı derlenip benchmark edildi
- Deney sürecinde
nixsayesinde paralel sürümler yeniden üretilebilir ve yalıtılmış biçimde korunabildi- Belirli bir derlemenin hangi derleyici ve bayraklarla üretildiğinden emin olunabildi
- Derleme matrisi tanımı da kısa soyutlamalarla yönetilebildi
- Hata düzeltme yaması uygulanmış özel bir LLVM derleyip, bu derleyiciyle Python derlemesi yapmak da yaklaşık 10 satır kodla mümkün oldu
- Dezavantajlar da varlığını sürdürdü
nix, genel yazılım kullanım biçiminden farklı yönlere sahip olduğu için, bu farklılıkların benchmark ya da sonuçları etkilemiş olma ihtimalini tamamen dışlamak zor- Örneğin nix varsayılan olarak projeleri belirli hardening bayraklarıyla derliyor ve bu bayrakların kuyruk çağrısı yorumlayıcısını orantısız biçimde etkilediği erken aşamada fark edildi
- Nix güçlü ölçeklenebilirlik ve özelleştirme sunuyor, ancak belirli özelleştirme yöntemlerini bulmak için çok sayıda deneme-yanılma ve
nixpkgskaynak kodu incelemesi gerekebildi
1 yorum
Hacker News yorumları
CPython’a kuyruk çağrısı yorumlayıcısını ekleyen PR’ın yazarıyım.
Öncelikle bu sorunun nedenini bulmak için neredeyse bir ay harcayan Nelson’a teşekkür ederim.
Temel çizgi için kullandığımız derleyicide böyle bir hata olacağını ne ben ne de muhtemelen CPython ekibi bekliyordu; büyük bir hata yaptığım için çok utanıyor ve özür diliyorum.
Bir özür yazısı da yayımladım: https://fidget-spinner.github.io/posts/apology-tail-call.htm...
%10~15 performans artışı diye duyurulmuştu, fakat hatasız bir derleyicide bunun %1~5’e daha yakın olduğu söyleniyor; o rakam da tamamen yanlış değil, yalnızca belirli koşullarda doğruymuş.
İyileştirmeyi yapmış, ölçmüş ve PR’ı da inceletmiş; yani yapılması gerekeni yapmış sayılır. Ölçümde kullanılan clang sürümündeki sorun nedeniyle rakamların yanlış anlaşılmaya yol açması, herkesin düşebileceği makul bir hata gibi görünüyor.
Yine de anlamlı bir performans iyileştirmesi sağladı ve bir derleyici regresyonunu da ortaya çıkardı; yanlış rakamlar bunların yanında küçük görünüyor. Bu işten gerçekten kimin zarar gördüğünden de emin değilim; özür gerektirecek bir durum gibi gelmiyor.
Python yorumlayıcısı gibi eski bir sistemde %3~5 kalıcı oluyorsa bu tek başına bile büyük bir başarıdır; bununla gurur duymak gayet yerinde.
Yaklaşık 30 yılın ardından, uzun süredir var olan sistemlerde anlamlı performans artışlarına, özellikle %1’i aşanlara, önce şüpheyle bakar oldum.
Gerçek iyileştirmeler elbette var, ama yaygın değiller; bazen de yalnızca zamanı başka bir yere taşımış oluyorsunuz ve bu benchmark’ta görünmüyor. Ayrıca benchmark’lar etkiyi izole etmek için kontrollü ortamlarda yapılır, oysa gerçek yazılımlar VM’lerde veya masaüstlerinde başka pek çok şeyle birlikte çalışır.
İzole bir ortamda bariz şekilde büyük görünen iyileştirmelerin üretim ortamına girince kaybolduğunu ya da negatife döndüğünü çok gördüm.
CPython’ın birçok ortamı hedeflemesi gerektiği için iş daha da zor; “üretimde hızlanmıyorsa gerçekten hızlanmamıştır” denebilecek tek bir üretim hedefi de yok. Böyle bir dünyada performansı iyileştirmeye çalışmak gerçekten zor.
Sonuçta performans ayarlama ve ölçüm çok zor; özür dilenecek şey olsa olsa bu gerçeği öğrenmiş olmaktır.
Yanılmaktan korkmamanızı umarım. Zaten herkes yanılır. Şimdiki gibi “galiba bunu biz batırdık” deyip, bununla nasıl başa çıkılacağını ve gelecekte nasıl önleneceğini bulmak yeterli.
[1] Bu yalnızca performansta değil, insanların süreçlerinde de yaygındır. Örneğin bir kod inceleme aracı ekibi “kod inceleme süresini %15 azaltarak herkesin iş akışını hızlandırdık” dese bile, gerçekte sistemin başka bir bölümünde daha fazla iş yaratmış, genel akışı hızlandırmamış ve %15’i ölçülmeyen bir yere taşımış olabilir.
Teoride, böyle bir kontrol akış grafiği ve profil olduğunda derleyicinin geleneksel
switch()tabanlı bir yorumlayıcı için en iyi kodu üretmeye yetecek bilgiye sahip olması gerekir. Ama pratikte işlev bu kadar büyük ve birbirine bağlı olduğunda derleyiciyle mücadele etmeye başlarsınız.Register’da tutmak istediğiniz önemli değişkenleri spill eder, fallback işlev çağrıları etrafına sıkıştırmak istediğiniz stack frame işlemlerini yukarı çeker, dallanma tahmini yüzünden ayırmak istediğiniz aynı kod yollarını birleştirir. Eldivenle piyano çalmak gibi hissettirebilir.
Burada da tam olarak o “aynı kod yolu birleştirme” gerçekleşti; “hatalı” derleyici aynı yolları birleştirerek performansı kötüleştirdi.
“Düzeltilmiş” derleyici artık bunu yapmıyor, ama o düzeltme de nihayetinde derleyicinin iç sezgisellerini ayarlamaya yakın bir şey. Bu derleyicinin ya da başka derleyicilerin ileride de sezgisellerini bizim lehimize olacak şekilde koruyacağının garantisi yok.
Buna karşılık kuyruk çağrısı yorumlayıcısı, istenen makine kodu desenini yorumlayıcının kendisinde ifade edebilir.
musttail,noinline,preserve_noneöznitelikleri birlikte kullanıldığında problem, optimize edici sezgisellerinden çok daha az etkilenilecek şekilde kısıtlanabilir.Bu yüzden kuyruk çağrısı yorumlayıcısının faydası basit bir %3~5 performans artışından fazlasıdır; bazı derleyicilerde bundan daha büyük, güvenilir performans artışı da sağlayabilir.
Benzer şeyleri önlemek için benchmark’lar iyileştirilebilir mi?
[0] https://github.com/faster-cpython/benchmarking-public
Benchmarking işini düzgün yapmak gerçekten delirtici derecede zor. İnsanları yanıltan çok fazla unsur var.
Yakın zamanda bir algoritmayı yaklaşık %15 hızlandırmanın bir yolunu bulduğumu sanmıştım. En azından tüm benchmark’lar öyle söylüyordu.
Ama test düzeneğine daha hızlı fonksiyonu kopyalayıp koyduğumda ve aslında onu hiç çağırmadan yalnızca eski yavaş sürümü çağırdığımda bile hâlâ %15 daha hızlıydı. Yani hiç çalıştırılmayan kod, asıl kodu hızlandırmış oldu.
Doğal olarak bu bir kod ve bellek yerleşimi meselesiydi; bir şeyler yer değiştirince CPU önbelleğine daha iyi oturmuştu.
Elde edilen hız artışının kodun gerçekten “daha iyi” olmasından mı, yoksa bir yerlerde daha iyi hizalamayı şans eseri yakalamaktan mı kaynaklandığını bilmek gerçekten zor.
Casey Muratori Substack’te bu konuda çok ilginç bir seri yazıyor.
Gerçek durumu kurcalayıp ortaya çıkaran yazara övgüler. Python 3.14’ün tail call interpreter’ı hâlâ iyi bir iyileştirme ve dil çalışma zamanlarında birkaç puanlık artış zor kazanılan bir başarıdır.
Sadece, sihirli bir %15 bedava öğle yemeği değildi.
Daha önemlisi, bu olay benchmarking’de titizliğin ve birden fazla ortamda test etmenin önemini iyi gösterdi. Herkese fayda sağlayabilecek bir derleyici hatasını da ortaya çıkardı.
Bir sonraki büyük performans artışı iddiasını yeniden kontrol ettirecek türden bir derin analiz. Üzerinde düşünülmesi gereken diğer soru şu: Ortalıkta dolaşan sayısız “%X daha hızlı” sonucunun ne kadarı aslında benchmark yapay etkisi ya da bilinmeyen regresyonlardan kaynaklanıyor?
Gelecekte bu tuzaklardan daha iyi kaçınmak için ne yapmalıyız?
Derleyicinin kendisi benchmark edilmiyor mu? Derleyici tarafındaki ya da Python tarafındaki mevcut benchmark’lar o derleyiciyi kullanmıyor muydu?
Bu, C’nin “makineye yakın” ya da “taşınabilir assembly” olduğu sözlerinin ne kadar isabetsiz olduğunu gösteren iyi bir örnek. Modern optimize ediciler, gözlemlenebilir bir etki yoksa mantığı cesurca değiştiriverir.
Yazıda da “clang-19, computed-goto interpreter’ı sonuç ikilisinin beklenen tüm değerleri üretmesi anlamında ‘doğru’ derliyor; ama aynı zamanda bu çıktı, optimizasyon niyetiyle tamamen ters düşüyor. Üstelik diğer derleyici sürümleri ‘saf’
switch()tabanlı interpreter’a optimizasyon uygulayarak, bizim kaynak kodu yeniden yazarak ‘amaçladığımız’ şeyle tam olarak aynı optimizasyonu gerçekleştiriyor” deniyordu.C’deki
a += 1ifadesinin sayısal değeri artırdığına güvenebilirsiniz; oysa C++’ta aynı ifade bellek ayırabilir, çağrı yığınını açabilir ya da bilinmeyen şeyler yapabilir. Benzer şekildea = "a"C’de basit bir pointer atamasıyken C++’ta bellek ayırma vb. gerçekleşebilir.“C taşınabilir assembly’dir” ifadesi, her cümlenin doğrudan eşdeğer makine koduna derlendiği anlamına gelmez.
Derleyicinin döngü yapısını kurcalamasıyla tail call interpreter’ın tamamının duyurulduğu kadar etkili olmaması şaşırtıcı değil.
Paranoyak interpreter uygulamaları yeniden doğrudan assembly yazmaya da dönebiliyor. LuaJIT, mimariler arasında taşınabilir kılmak üzere çok verimli bir assembly döngüsü uygulaması için bir makro sistemi geliştirmesiyle ünlü. Bu yüzden böyle şeylerle uğraşmak da eğlenceli.
Birkaç yıl önce popüler interpreter döngüsü uygulama yöntemleri üzerine bir yazı ve testler de hazırlamıştım:
https://github.com/vkazanov/bytecode-interpreters-post
Modern branch predictor’lar, çalışma aralığı yeterince uzunsa ve yorumlanan kodun kendi davranışı kararlıysa tek bir dolaylı jump’ı bile neredeyse tam doğru tahmin edebiliyor.
Bunu hem gerçek donanımda hem de belirli simülasyon branch predictor’larında inceleyen bir makale var: https://inria.hal.science/hal-01100647/document
Bu projede yaptığım deneyler de anekdot düzeyinde aynı sonucu destekliyor. Yazıya koyamadım ama donanım CPU sayaçları ve
perf statile birkaç interpreter’a baktım; branch yanlış tahminlerinin baskın faktör olarak görünmediğini gördüm.Python derleme performansını değerlendirmek son derece zor. Çünkü performansı iyileştirebilecek çok fazla build tekniği var.
Yakın zamanda astral tarafı da conda-forge derlemesinin çoğundan belirgin biçimde daha hızlı olduğunu gösterirken bu sorunla karşılaştı:
https://github.com/astral-sh/python-build-standalone/pull/54...
Tail call interpreter’ın mevcut diğer build optimizasyonlarıyla birlikte kullanıldığında nasıl davrandığını merak ediyorum.
Yazar, genetik algoritma ile çeşitli derleyici ve optimizasyon bayrağı kombinasyonlarını deniyor.
İlgili tartışmalar:
https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-... --> https://news.ycombinator.com/item?id=42999672 (66 puan | 25 gün önce | 22 yorum)
https://blog.reverberate.org/2025/02/10/tail-call-updates.ht... --> https://news.ycombinator.com/item?id=43076088 (124 puan | 18 gün önce | 92 yorum)
Güzel yazı. Bir ayrıntı gözüme çarptı
Referans verilen yazılardan biri olan https://simonwillison.net/2025/Feb/13/python-3140a5/ içinde “3.14.0a5, benchmark’ta 3.13’ten 1,12 kat hızlıydı; aşırı yük altındaki M2 MacBook Pro’mda” diye yazmış
Bu kısım epey kafa karıştırıcı. Bilgisayar başka süreçler yüzünden aşırı yük altındayken benchmark çalıştırdığı anlamına mı geliyor? Öyleyse sonuç tamamen güvenilmez olmaz mı?
Bu tür benchmark’ların dış değişkenleri ortadan kaldırmak için çok kontrollü ortamlarda yapıldığını sanıyordum
CPython tarafı için https://github.com/faster-cpython/benchmarking-public adresine bakılabilir
Burada bazıları %10’u “büyük”, %1’i “normal” diye niteliyor ama çift özyinelemeli Fibonacci’de kısmi inline etme gibi bir optimizasyon gerçek iş yükünü ve süreyi üstel olarak azaltabilir
İki basamaklı argümanlarda 10 kattan fazla, yani binlerce yüzde bile mümkün. Kesin konuşmak gerekirse bu, problem boyutuna değil özyineleme derinliği farkına göre üstel [1]
C derleyicileri de kod inline etme metriklerine karşı çok hassas davranabildiğinden, bu muazzam hızlanmanın gerçekten ortaya çıkıp çıkmaması kodun biçimine çok duyarlı olabilir
Bu yüzden sorunun bir kısmı CPU’ların çok incelikli ve karmaşık hale gelmiş olması; ama diğer bir yönü de
-O0veya-O1ötesindeki derleyicilerin de incelikli ve karmaşık hale gelmiş olmasıBu yazı iyi ve okumaya değer, ama iki karmaşık şey etkileşime girdiğinde çok şaşırtıcı sonuçlar çıkabileceğinin sayısız örneklerinden biri de. Bu, bilişimin dışında da geçerli
İnsanlar bu ders kaç kez tekrarlanırsa tekrarlansın aşırı basitleştirme eğiliminde
Ayrıca yazıda en azından Intel ve Apple M1 olmak üzere iki CPU, gcc ve clang olmak üzere iki derleyici kullanılıyor; fakat gerçek dağıtım ortamlarında çok daha fazla Intel, AMD, ARM nesli ve uygulaması ile başka derleyiciler bulunabilir. Toplam karmaşıklığın yalnızca çok küçük bir kısmı örneklenmiş oluyor
Daha bilimsel yapmak için, özellikle “1,01 kat” gibi farklarda zaman ölçümlerinde bir tür hata çubuğu olmalı. Ortalamanın standart sapması ya da bu durumda belki minimumun standart sapması daha iyi olabilir [2]
Ölçüm hatasını azaltmak için muhtemelen işletim sisteminde CPU çekirdeği sabitlemeli zamanlama da gerekir
[1] https://stackoverflow.com/questions/360748/computational-com...
[2] https://github.com/c-blake/bu/blob/main/doc/tim.md
Yakın zamanda Python 3.9’dan 3.13’e kadar benchmark yaptım; 3.11’e kadar sürekli iyileşiyordu
Ama Python 3.12 ve 3.13, 3.11’den yaklaşık %10 daha yavaştı
Kendi yazdığım benchmark’ın yeterince iyi olmadığını düşündüm ama yine de çekirdek servislere dağıttım ve topladığım metriklerde de aynı değişim göründü
Aynı sorunu yaşayan var mı?
[0]: https://github.com/python/cpython/issues/123540