Alternatif Uygulama Sorunu
(pointersgonewild.com)- Dil çalışma zamanı veya JIT gibi alternatif uygulamalar, performansları daha iyi olsa bile asıl uygulamadaki değişimleri ve kullanıcı beklentilerini sürekli takip etmek zorunda oldukları için benimsenmeleri sınırlı kalabilir
- PyPy, LuaJIT ve TruffleRuby hızlı çalışma performansı gösterdi, ancak uyumluluk açığı ve yeni özellikleri takip etme yükü gerçek dağıtımın önünde engel oldu
- YJIT, ayrı bir Ruby uygulaması olmak yerine CRuby’nin içine girme yolunu seçti; böylece en baştan CRuby özellikleriyle %100 uyumlu olacak şekilde tasarlandı ve Shopify, Discourse, GitHub gibi yerlerde dağıtıma girdi
- Crystal gibi mevcut bir dile çok benzeyen ama uyumlu olmayan bir tercih, kullanıcıların sürekli “neredeyse Ruby ama Ruby değil” farklarıyla karşılaşmasına yol açabilir
- JSON ayrıştırıcıları veya JavaScript gibi açık standartların uygulamalardan ayrıldığı alanlarda alternatif uygulamaların yükü azalır; ancak asıl uygulamanın fiilen standart olduğu ekosistemlerde farklı bir strateji gerekir
Tekrarlanan alternatif uygulama tuzağı
- Yazılım dünyasında, mevcut bir sistemin daha iyi bir alternatif uygulaması olarak başlayan projelerin asıl uygulamanın gölgesinde kalması sıkça tekrar eder
- Alternatif uygulamalar; özellikler, performans, ekosistem ve kullanıcı beklentileri açısından standart gibi kabul edilen asıl uygulama (canonical implementation) ile karşılaştırılır
- Asıl uygulama sürekli değiştiğinde, alternatif uygulama kendi yönünü belirlemekten çok bu değişimi takip etmeye enerji harcamak zorunda kalır
- Geleneksel olarak yorumlayıcı olan bir dile JIT uygulaması eklendiğinde, yeni özellikler yorumlayıcıya daha hızlı girmeye eğilimli olur ve JIT tarafının takip yükü artar
PyPy, LuaJIT ve TruffleRuby’de görülen örüntü
- PyPy, Python için gelişmiş bir JIT derleyicisiydi ve CPython’a kıyasla büyük hız artışları sağlayabiliyordu, ancak gerçek kullanım çok sınırlı kaldı
- Python, düzenli olarak yeni CPython sürümleri ve özellikleri çıkan hareketli bir hedef
- PyPy bunu takip etmekte zorlandı ve sürekli Python’un birkaç sürüm gerisinde kaldı
- Python yazılımlarını PyPy ile uyumlu tutmak için kullanılabilecek Python özellikleri sınırlanıyor ve çoğu Python geliştiricisi bununla uğraşmak istemiyor
- LuaJIT, temel yorumlayıcı tabanlı Lua uygulamasına göre büyük performans artışı sağladı; yüksek takdir gördü ve bir ölçüde gerçek benimsenme elde etti
- LuaJIT’in yaratıcısı Mike Pall, birçok kişi tarafından olağanüstü bir programcı olarak değerlendiriliyor
- Lua dili yeni özellikler eklemeyi sürdürdükçe LuaJIT de birkaç sürüm geride kaldı
- Bu nedenle bazı Lua kullanıcıları LuaJIT kullanmaktan kaçınıyor
- Lua minimalizmiyle bilinen bir dil olsa da, yeni özellik eklemeyi yavaşlatma ya da Mike Pall ile koordinasyon kurma yönünde bir çaba gösterilmedi
- TruffleRuby, Ruby JIT’leri arasında en etkileyici performans rakamlarını ortaya koydu, ancak dağıtımı sınırlı kaldı
- Bunun pratik nedenlerinden biri, TruffleRuby’nin ısınma süresinin CRuby’ye göre çok daha uzun olmasıydı
- CRuby yeni özellikler eklemeyi sürdürürken TruffleRuby katkıcıları da bunları takip etmek için çaba harcamak zorunda kaldı
- Ruby kullanıcıları CRuby’yi asıl uygulama olarak görüyor ve tam uyumlu olmayan uygulamaları değerlendirmeye pek değer bulmuyor
YJIT’in seçtiği farklı yol
- YJIT de başka bir Ruby JIT’i olarak başladı, ancak ayrı bir uygulama yerine doğrudan CRuby’nin kendi içine inşa edildi
- Bu tercih çeşitli tasarım ödünleşimlerine yol açtı, ancak YJIT’in en baştan tüm CRuby özellikleriyle %100 uyumlu olmasını sağladı
- Bugün YJIT, Ruby’nin “resmî” JIT’i ve Shopify, Discourse, GitHub gibi yerlerde dağıtımda
- github.com’u ya da bir Shopify mağazasını ziyaret eden kullanıcılar aslında YJIT ile etkileşime girmiş oldu
- Şimdiye kadarki Ruby JIT derleyicileri içinde en büyük başarıyı YJIT elde etti ve bu başarıda uyumluluk kilit rol oynadı
“Yenemiyorsan katıl” tek başına yetmez
- Kendinizi alternatif uygulama olarak konumlandırırsanız, asıl uygulamanın gölgesinde bitmeyen bir yetişme oyununa girmeniz çok olasıdır
- Asıl proje gelişmeyi sürdürdükçe, alternatif uygulama kendi yönü üzerinde sınırlı karar gücüne sahip olarak onun arkasından gitmek zorunda kalır
- Asıl uygulamaya katılmak daha iyi sonuç verebilir, ancak bu da her durumu tek başına çözmez
- Ruby ekosistemindeki Crystal, Ruby’ye benzeyen sözdizimine sahip, statik derlenen ve tip çıkarımı kullanan bir dildir
- Crystal, bilerek Ruby uyumluluğunu hedeflemedi ve Ruby’den ayrılan bir yol izledi
- Ruby geliştiricileri için bu, “neredeyse Ruby ama tam Ruby değil” bir dil gibi görünür; pratikte çok sayıda ince fark ve uyumsuzluk içerir
- Bu benzerlik kullanıcı beklentilerini bulanıklaştırır ve kafa karışıklığı yaratır
- Crystal, en başından Ruby’ye benzediği şeklinde pazarlanmasaydı daha iyi sonuç almış olabilirdi
Rekabetten kaçınmak ve kendi yönüne sahip olmak
- Peter Thiel’in “competition is for losers” sözü, gereksiz yere rekabet etmek zorunda kalacağınız bir konuma kendinizi sokmamanız gerektiği bağlamında kullanılıyor
- Buradan, yeni bir programlama dili yapacaksanız Python’un bir alt kümesini ya da mevcut bir dile yüzeysel olarak aşırı yakın bir şeyi yapmaya çalışmamanız gerektiği yönünde bir öğüt çıkıyor
- Kendinize ait bir şey inşa ederseniz, başka bir uygulamanın performansına, özellik kümesine ve kütüphane ekosistemine yetişme beklentisine bağlanmadan sistemi kendi hızınız ve yönünüz doğrultusunda geliştirebilirsiniz
- Bu öğüt, dilin ya da sistemin bir asıl uygulamaya sahip olduğu durumlar için geçerlidir
- Açık standartların bulunduğu alanlar istisna olabilir
- JSON ayrıştırıcıları, görece küçük ve hızlı değişmeyen net bir spesifikasyona sahip oldukları için bağımsız uygulamaya uygundur
- JavaScript’te tarayıcı tabanlı birden çok uygulama vardır; bu da JS spesifikasyonunu yöneten harici bir standart kurumu olduğu için mümkündür
- JS standardı üzerinde çalışanlar, JIT derleme uygulamalarında performansın kritik olduğunu anlıyor ve dilin evrimini buna göre yönlendiriyor
- Mümkün olduğunca hızlı biçimde olabildiğince çok yeni özellik ekleme oyunu oynamıyorlar
1 yorum
Hacker News yorumları
OP'nin kaçırdığı önemli bir nokta daha var. Alternatif uygulama yaptığınızda mimari genellikle referans uygulamadan farklı olur ve referans uygulamada kolay olan bir şey sizin uygulamanızda çok zor olabilir.
Örneğin, finansal raporlar için kullanılan tescilli bir yazılımın belgeleri tuhaf bir ikili biçimde kaydettiğini düşünelim. Ücretsiz bir alternatif geliştirirken tüm belgeyi belleğe okuyan ve kaydederken tüm dosyayı baştan yazan bir yapı seçmiş olun; oysa orijinalin RAM'in az olduğu dönemlerde geliştirildiğini, kullanıcının üzerinde çalıştığı yalnızca ilgili bölümü okuyup yazdığını ve yerinde düzenleme bile yapabildiğini varsayabiliriz.
Daha sonra orijinal, belgelere ek dosya koyma özelliği eklerse, yatırımcı görüşmesi kayıtları veya yüzlerce sayfalık taranmış PDF'ler gibi büyük dosyalar da bölüm bazlı yükleme sayesinde sorunsuz çalışır. Buna karşılık sizin uygulamanız tüm belgeyi serileştirmeden geri açtığı için belge kullanıcının RAM'inden büyük olduğu anda sorun çıkar; orijinalde tek bir geliştiricinin bir haftada bitireceği bir değişiklik yüzünden tüm yazılımı yeniden tasarlamanız gerekebilir.
Tersine, orijinalde uygulanması daha zor olup ikinci uygulamada önemsiz hale gelen özellikler de vardı. Büyük yeniden tasarım yalnızca bir kez oldu; o da üstel patlamaya yol açan bir varsayıma sahip orijinal uygulamadaydı.
Açık bir örnek olarak Linux'ta Windows uygulamalarını çalıştırma sorunu verilebilir. Linux çekirdeği NT'den tamamen farklı bir uygulamadır ve uyumluluğu hedeflemez; ama Windows uygulamalarını çalıştırmak için tüm çekirdeğin yeniden tasarlanması gerekmez. Nispeten genel birkaç çekirdek özelliği ve kullanıcı alanında bir uyumluluk katmanı yeterlidir. Wine'ı yazmak ve sürdürmek çok emek ister, ama Windows'un kendi uygulamasından çok daha azdır ve Windows uyumluluğunu hedeflememiş bir platform üzerinde çalışır. Ancak yazıda söylendiği gibi Windows'u sürekli takip etmek gerekir; referans uygulamadaki hatalara bağımlı kodlar bile ortaya çıktığından, hatalarla bile uyumlu olmak için önce hangi hataları uygulamanız gerektiğini bulmanız gerekir.
Bu yüzden herhangi bir kod, çalışma sırasında başka bir şeyi değiştirebilir. Pratikte bu çok kullanılmaz, ama kaldırmaya kalkarsanız insanlar ayağa kalkar. Python'ı gerçekten derleyen bir uygulama, bir iş parçacığının aniden başka bir iş parçacığının altındaki bir şeyi değiştirmesi durumunu bile ele almak zorundadır.
Bu, küçük bir şirketin büyük bir şirketin yükünü artırma yollarından biridir; teknik borcu daha az ihmal etmiş bir şirket de bunu yapabilir. Aynı zamanda teknik borcu yönetime açıkça gösterebilmenin nadir anlarından biridir. Çünkü “Bu özelliği uygulamak bizde Acme'den daha uzun sürüyor” diyebilirsiniz.
“Python'ın bir alt kümesini yapmaya çalışmayın” iddiasına katılıyorum. “Python ama X daha iyi” diye pazarlanan projeler referans uygulamayla rekabet etmekte her zaman zorlanır; özellikle X hız ise daha da öyledir. Dinamik tipli dil kullanan insanlar sonuçta çoğu zaman yürütme hızını pek umursamaz.
Ama alternatif uygulamalar her zaman başarısız olmaz. MicroPython, Python 3.4 düzeyi ve üzerini neredeyse desteklememesine rağmen epey başarılı görünüyor. Çünkü mikrodenetleyicilerde çalışmak üzere tasarlandı; CPython ile değil, başka mikrodenetleyici programlama ortamlarıyla rekabet ediyor.
Yine de MicroPython bakımcıları daha yeni Python özellikleri için çok talep alıyor olmalı. Bir dönem uygulama içine gömmek için hafif ve gömülebilirliğe odaklı bir Python alternatif uygulaması düşünmüştüm; bu durumda da fikir CPython ile değil Lua ile rekabet etmekti. Ama bir numaralı özellik talebi “NumPy destekliyor musunuz?” oldu.
Aynı zamanda CPython tarafında da ön yüzde daha iyi çalışması için çalışmalar yapılıyor gibi; temel acı noktası ise paket boyutu.
Bir startup kurarken benzer bir şey öğrendim. Yeniden yapsaydım alanımızdaki masaya oturmak için gereken temel özelliklerden aktif biçimde kaçınırdım.
Bunun yerine kurumsal tipte gereksinimleri mimarimizin destekleyebileceğine güven verecek asgari kısmı yapıp, tüm odağı “aa, bunun nereye kadar gidebileceğini görüyorum” tepkisi aldıracak farklılaştırıcı unsurlara vermeliydik. “Sadece X'in kopyası işte” tepkisi alan özelliklere değil.
Söylediğin strateji, temel zorunlu özellikleri uygulayıp sonrasındaki “olağan” genişletme özelliklerinde fazla derine gitmemeye daha yakın. İlginç özelliklerle yeniden iletişim kurulmasını sağlarken, aynı anda zorunlu unsurlar eksik diye elenmeyecek kadar temel özelliğe sahip olmak.
Tüm wrapper kodlar için benzer hissediyorum. Birinin “bu API'nin dahili kullanım için bir sürümüne ihtiyacımız var” dediği durumlar oluyor.
Gerekçeler değişiyor ama genelde “resmî API'yi düzgün kullanacaklarına güvenemeyiz” gibi bir şey oluyor. Bu anlaşılabilir, ancak dahili sürüm daha az standart, belgeleri de daha kötü oluyor.
Bazen gerekçe “ek özelliklere ihtiyacımız var” oluyor; öyleyse tüm API'yi sarmak yerine sadece 3 fonksiyon eklemek yeterli. Zamanla kod tabanının %99'u bir polyfill'e dönüşebilir.
Özetle, varsayılanları kullanmazsanız ileride kod tabanını devralan kişiye büyük acı yaşatırsınız.
Örneğin Zig ile Python native modülleri yazmaya yarayan ziggy-pydust kütüphanesi, sıradan Python.h import'una göre kesinlikle daha hoş görünüyor. Yine de henüz uygulanmamış ama Python.h'de bulunan fonksiyonlara doğrudan erişen bir
.ffide var.Böyle bir seçenek yoksa genelde o tür kütüphaneyi bırakıp orijinali tercih ediyorum. Bunun da bir anlamda wrapper, yani native modül olduğu doğru; geliştirme hızı için bazen doğrudan ctypes kullanmak daha iyi olabiliyor.
İyi bir yazı ve harika dersler var, ama temel bir bileşen eksik. Bu, herhangi bir ürünün rakip alternatifiyle de benzer.
Amazon'un insanların alışık olduğu fiziksel kitapçılara sahip olmadığı için başarısız olduğunu söylemeye benziyor; oysa gerçekte öyle olmadı.
Bu JIT alternatiflerinin başarısız olup sürekli yakalamaya çalışmasının nedeni, pratikte X dili geliştiricilerinin çoğunun JIT'i o kadar önemli görmemesi. Daha doğrusu, dil özelliklerini ve birlikte çalışabilirliği JIT'ten daha önemli görüyorlar.
Bu yüzden rekabet etmek yerine “katılan” ürün kazanıyor. Çünkü daha yüksek güvenilirlik ya da birlikte çalışabilirlik sunamıyor.
Uzun süredir diller ve derleyicilerle uğraşıyorum; bu yazı çok ilginçti. Aynı düşünceyi başka şekilde söylersek, dil basit derleme hızından çok daha fazlasıdır.
Derleme hızı çok önemlidir ve kesinlikle ilk 10 boyutun içindedir. Özellikle derleme hızını artırmak geliştirici geri bildirim döngüsünü hızlandırır ve çekirdek ekibin diğer tüm boyutları da daha hızlı iyileştirmesini sağlar.
Yine de programlama dillerinde çok önemli 30'dan fazla boyut daha var.
Bu tür projelerin nasıl başarılı olacağına dair sonuç iyi, ancak pek çok projenin neden tutmadığı konusunda hâlâ az değinilen bir etken var: alternatif uygulamaların uyumluluğu çoğu zaman iddia edilenden daha düşük oluyor; eski dil özelliklerinde bile.
Örneğin Ruby ve Python uygulamalarında bağımlılıkların bir yerinde native C uzantısı bulunması çok yaygın; bildiğim kadarıyla başlıca alternatif uygulamalar bunları hiç desteklemedi. Denemeler oldu ama bariz teknik nedenlerle iyi sonuç vermedi; kütüphanelerin birden çok uygulama sunmasını bekleyen alternatiflerin tarihi de pek sorunsuz değil.
Buna, bu dillerin performans faktörünün CPU'dan çok I/O olduğu CRUD web sitelerinde sık kullanıldığı gerçeğini de ekleyince, daha hızlı alternatiflerin cazibesi çok daha azalıyor.
Gerçekten harika bir yazı. Teknolojinin sosyolojisi çok ilginç.
Dil uygulaması bakımcıları, kullanıcıların yararına yeni özellikler tasarlayıp dağıtırken mümkün olan en fazla esnekliği ister. Bir özelliği çıkarmadan önce birden fazla uygulamanın mutabakatını almak zorunda kalıp yavaşlatılmak istemezler. Örnek olarak JavaScript'in gelişiminin yıllarca ne kadar buzul gibi yavaş hissettirdiğine ve TypeScript'in buna kıyasla ne kadar hızlı evrildiğine bakılabilir.
Aynı zamanda alternatif uygulamalar, bir dil ekosisteminin sağlam olduğunun işareti olabilir; bu yüzden avantajları da var. Alternatif uygulama gerçekten iyiyse, yalnızca bazı niş ihtiyaçları olan kullanıcılar için iyi olsa bile ekosisteme gerçek değer katabilir.
Bu nedenle bir dil tasarımcısı ya da bakımcısıysanız alternatif uygulamalara aktif olarak düşmanca yaklaşmayabilirsiniz, ama dezavantajları var. Kullanıcılardan gelen geri bildirim genelde yeni özellikler çıkarıp dili geliştirme yönünde olacaktır. PyPy, IronRuby, LuaJIT vb. yetişebilsin diye yavaşlamanızı isteyen pek çok talep gelmeyecektir.
Dil tüketicileri hangi uygulama üzerine inşa edeceklerini seçerken en büyük öncelik genelde emniyet ve kararlılıktır. Kimse milyon satırlık kod tabanının, bir zamanlar çok zeki bir doktora öğrencisinin yaptığı ama artık başka projelere geçmiş olduğu alternatif uygulamanın ince davranış özelliklerine bağımlı hâle gelmesini istemez. Bu yüzden kullanıcılar en çok kullanılan uygulamaya yönelir ve bu gerçek de başka kullanıcıları çeken güçlü bir pozitif geri besleme döngüsü oluşturur.
Sonuçta ters yönde iten güçlü bir kuvvet yoksa çoğu dil tek bir referans uygulamada yakınsar. Bunun iyi bir şey olduğu da savunulabilir. Çünkü dil uygulamasına harcanan mühendislik emeğinin neredeyse tamamı birden çok uygulama arasında bölünmek yerine tüm kullanıcılara fayda sağlar. Elbette dezavantajı, uygulamanın yerel optimuma takılıp kalabilmesidir.
LuaJIT'ten bahsedilmiş, ama aynı zamanda her zaman yazının sonucuna göre gitmediğine dair bir örnek. Pek çok kişi ve proje LuaJIT'i Lua'ya bilinçli olarak tercih etti.
luajittexçalıştırılabilir dosyası daluatexile yan yana kolayca bulunabiliyor.Daha az popüler bir görüş olabilir ama insanlar, kendileri de dahil olmak üzere, zaman zaman egolarını kontrol etmeli. Mevcut bir açık kaynak projeye katkıda bulunmaktansa “benim” olan paralel bir proje oluşturmak daha kolay olabilir; ama bunu kimin için yaptığını sormak gerekir
Projenin bakımcıları için mi, projenin kendisi için mi, kullanıcılar için mi, yoksa kendi egon için mi? Son maddeye kızıyorsan, muhtemelen bununla ilgisi vardır
Mevcut bir dile JIT eklemek büyük bir iştir; bu yüzden referans uygulamanın kabul edeceği standart da yüksek olacaktır. Yine de hedefin o yönde olması gerektiğini düşünüyorum. Forklamak ya da sıfırdan yapmak da büyük işler yapma özgürlüğü verir, ama çoğu zaman geçici bir şey olarak görülmeli
Hedef, neler yapabildiğini göstermekse muhtemelen fazla ileri gidemezsin. Hedef daha iyisini yapmaksa, başkalarının kısıtları içinde çalışmayı öğrenirsin
Bence bu yazı, Python, Lua ve Ruby’nin bu yaklaşımı seçerek birçok insanı nasıl hayal kırıklığına uğrattığını gösteriyor. Sonuç olarak binlerce geliştirici ve milyonlarca kullanıcı daha yavaş geliştirmeye ve daha yavaş yazılıma katlanmak zorunda kaldı. İmkânsız olduğu için değil, idari olarak bunu yapmaya teşvik olmadığı için