2 puan yazan GN⁺ 2024-07-31 | 1 yorum | WhatsApp'ta paylaş
  • Porffor, JavaScript’i çalışma anında değil önceden derleyerek WebAssembly ve native binary üreten bir araştırma projesidir
  • Bir interpreter’ı birlikte paketlemeyen yaklaşımı sayesinde, mevcut JS→Wasm projelerine göre çıktının 10–30 kat daha küçük ve hızlı olmasını hedefler
  • Native build’lerde de runtime paketlenmediği için binary boyutu 1000 kata kadar küçülebilir; örnekte yaklaşık 90 MB’tan 100 KB’ın altına düşüyor
  • JS ile yazılmıştır, eval içermez ve TypeScript’i ayrı bir build aşaması olmadan native olarak destekleyen bir yapı sunduğunu öne çıkarır
  • AOT, statik analiz optimizasyonu ve çalıştırma öncesi derleme için avantajlıdır; ancak eval gibi dinamik JS değerlendirmeleri zordur ve henüz birçok JS’in çalışmadığı erken aşamadadır

Porffor’un oluşturduğu çalıştırma biçimi

  • Porffor, JavaScript’i WebAssembly ve native binary olarak Ahead-of-Time derleyen bir araştırma projesidir
  • Bu sayfayı Porffor ile derlenmiş bir TypeScript binary’si sunuyor
  • Baştan itibaren AOT düşünülerek yazıldığı için, mevcut JS çalıştırma biçimlerinde zor olan optimizasyonları denemeye uygun bir yapıya sahiptir

WebAssembly ve native çıktılar arasındaki fark

  • JS → Wasm

    • Porffor’un WebAssembly çıktısı, mevcut JS→Wasm projelerine göre 10–30 kat daha küçük ve hızlıdır
    • Temel fark, JS’i doğrudan derlemesi ve interpreter’ı bundle etmemesidir
    • JS’i Wasm üzerinde çalıştırmak sandbox içinde çalıştırmayı mümkün kılar; ancak büyük performans kayıpları doğurabilir. Porffor bu maliyeti azaltmaya odaklanır
    • Uygulanabilir kullanım örnekleri:
      • Sunucu tarafı JS hosting: Edge runtime’larda Wasm sandboxing ile aşırı izolasyona gerek kalmadan güvenli çalıştırma sunabilir
      • AOT’nin düşük overhead’i, JIT’e kıyasla aynı donanımda daha fazla müşteriyi minimum performans kaybıyla çalıştırma olasılığı yaratır
      • Tersine mühendisliğe direnç: Hassas JS için derlenmiş kod, obfuscation’a kıyasla tersine mühendisliği daha zor hale getirebilir
  • JS → Native

    • Runtime paketlemeden JS’i gerçekten derlediği için binary boyutu 1000 kata kadar küçülebilir
    • Örnek boyut yaklaşık 90 MB → 100 KB altıdır
    • İçeride JS’i önce C’ye, ardından native’e derlediğinden, C’nin kullanılabildiği yerlerde JS de kullanılabilir
    • Uygulanabilir kullanım örnekleri:
      • Embedded sistemler, oyun konsolları vb. ortamlarda hızlı JS çalıştırma
      • 1 MB’ın altında tek tıkla çalıştırılabilir dosyaya derlenen küçük JS CLI uygulamaları

AOT’nin sağladığı avantajlar ve kısıtlar

  • Geleneksel interpreter’lar veya çok aşamalı JIT’ler, başlangıç süresi ile JS performansı arasında denge kurmak zorundadır
  • AOT önce derleyip sonra çalıştırdığı için derleme hızı geliştirici deneyimi açısından önemlidir, ancak kullanıcı deneyimini etkilemez
  • Bu yöntem, C++ ve Rust’ta olduğu gibi statik analiz tabanlı optimizasyonlar yapmaya alan açar
  • Başlıca dezavantajı, eval gibi dinamik JS değerlendirmelerinin olmaması ve JS motorunun yeniden yapılmasını gerektirmesidir
  • Henüz erken aşamada olduğundan birçok JS çalışmaz; ancak iyileştirme çalışmaları devam etmektedir
  • ECMAScript uyumluluğundaki ilerlemeyi izlemek için her commit’te resmi test paketi Test262 çalıştırılır

1 yorum

 
GN⁺ 2024-07-31
Hacker News görüşleri
  • Porffor'un ana geliştiricisi Oliver, Porffor üzerinde tam zamanlı çalışacağını duyurdu: https://x.com/canadahonk/status/1818347311417938237

  • Benzer bir şeyi düşünmüştüm ama JavaScript'te çok daha iyi performans elde etmenin zor olduğunu düşünüyorum. Muhtemelen yapılabilecek en iyi şey, JS'yi V8 C++ çağrılarına transpile etmek olurdu
    Gerçekten etkileyici optimizasyonlar, TypeScript veya ona yakın bir şeyi derlerken ortaya çıkar. Tiplerden yararlanırsanız büyük kazanç elde edebilirsiniz ve tipi olmayan kısımlar varsayılan olarak yavaş JS çağrılarına düşer. Arayüzler sanal fonksiyon tablolarına veya doğrudan çağrılara indirgenebilir ve map yerine struct'lar üzerinde çalışılabilir. Int ve Float tipleri tanımlanıp gerektiğinde Number'a düşürülürken register içinde tutulabilir
    Asıl sorun, TS ve V8'in ikisinin de hızlı değişen standart dışı hedefler olması. Böyle projeler büyük bir ekip gerektirir ve uyumluluğu korumak başlı başına ayrı bir iş haline gelir

    • Ek uzantılar olmadan TypeScript sanıldığı kadar yardımcı olmaz. Zaten başlangıçta bu amaç için tasarlanmamıştı
      Basit bir örnek olarak, TypeScript tamsayı ile kayan noktalıyı ayırmaz; ikisini de number olarak ele alır. Bu yüzden tüm dizi erişimlerinde tür dönüşümü gerekir. Statik derlemeye yardımcı olacak şekilde tasarlanmış bir TypeScript olsaydı, muhtemelen bu ayrım olurdu
      Daha büyük sorun ise TypeScript'in yapısal alt türleme özelliği. Bu özellik yüzünden derleyicinin, bir fonksiyona geçirilen ilkel olmayan argümanların fiziksel yapısını statik olarak belirlemesi pratikte imkansızdır. JIT dinamik şekil analizi yapabildiği için, her alan erişiminde JIT'ten daha kötü performans çıkabilir
    • Bir Porffor katkıcısı olarak buna katılmıyorum. JavaScript tarafında da derleme zamanında iyileştirme için oldukça fazla alan var
      JS için statik tip analiz araçları oluşturma konusunda çok iş yapıldı ve oldukça kapsamlı analizler de mümkün. Aklıma gelen örneklerden biri, biraz eski olsa da, TAJS
    • Bu fikirle bir ölçüde ilişkili bir proje olarak AssemblyScript var: https://www.assemblyscript.org
    • ECMAScript 4, dile daha iyi tipler ekleme girişimiydi ama ne yazık ki uzun zaman önce başarısız oldu
      TypeScript'te de integer gibi bir tip belirtilebilse güzel olurdu. Normal TS→JS derlemesinde const val: int, const val: number ile aynı şekilde ele alınsa bile, TS'yi anlayan modern bir runtime bu ek bilgiyi kullanabilirdi
      const counter: Number gibi bir sözdiziminin kabul edilip edilemeyeceğini merak ediyorum
    • “Bunu düşündüm ama daha iyi performans elde etmek zor” dedikten sonra, sitenin ana sayfasının üst kısmında zaten açıkça anlatılan şeyleri talep eden bir yaklaşımdan söz ediyor
      Site mi değişti, yoksa ben mi bir şeyi kaçırdım bilmiyorum
  • windmill.dev'de kullanıcılar kod dağıtırken Bun build kullanılarak script ve tüm bağımlılıkları tek bir JS dosyasında paketleniyor; bu dosya yüklenerek cold start ve bellek kullanımı iyileştiriliyor. Paket boyutu nedeniyle çıktı S3'te saklanıyor
    Her şeyi native olarak paketleyebilseydiniz oyun tamamen değişirdi. Bun'ın cold start'ı ne kadar iyi olursa olsun, doğrudan küçük bir binary olarak native çalıştırmayı geçmesi zor

    • Bir geliştirici olarak katılıyorum. Bu, Porffor'un potansiyel olarak yardımcı olabileceği ilginç bir kullanım senaryosu gibi görünüyor. Bir gün konuşmak güzel olurdu
  • Daha fazla JS runtime’ının Wasm’a yaklaşımını görmek güzel. Bu proje, React Native projelerinde iOS ve Android hızını artırmak için Facebook’un JS motoru olan Static Hermes’i hatırlatıyor.
    İkisi de JS test262 uyumluluğunu hedefliyor; Porffor hem native hem de Wasm çıktılarını desteklerken Static Hermes şu anda ağırlıklı olarak native çıktıya odaklanıyor. Porffor saf JS ile yazılmış ve kendisini derleyebilme yönünde ilerliyor; Static Hermes ise LLVM’e dayanıyor. Porffor’da async/promise/await desteği hâlâ sınırlıyken Static Hermes bunu bazı kısıtlarla destekliyor. Static Hermes C++ ile, Porffor ise ağırlıklı olarak JS ile yazılmış. İkisi de TypeScript’i destekliyor ancak Static Hermes TS AST’yi Flow’a transpile ediyor, Porffor ise bunu yerel olarak destekliyor. Static Hermes’te eval gibi derlenmesi zor JS durumları için yedek bir interpreter var, Porffor ise yalnızca ahead-of-time derlemeyi destekliyor.
    Genel olarak bu projenin ivme kazanıp edge’deki JavaScript motorlarını daha hızlı hâle getirip getiremeyeceğini görmek heyecan verici. Wasmer’den Syrus olarak bırakıyorum.
    https://github.com/facebook/hermes/discussions/1137
    https://github.com/tc39/test262
    https://wasmer.io

    • Bu arada Static Hermes, JS’yi WASM’e derleme özelliğini tamamen destekliyor. Mevcut LLVM backend’i olduğu için bu özellik neredeyse bedavaya geliyor. Örnek için https://x.com/tmikov/status/1706138872412074204 bağlantısına bakabilirsiniz.
      Ancak bu bizim odak noktamız değil; esas olarak React Native’e odaklanıyoruz. O ortamda WASM’in pek bir anlamı yok.
      Static Hermes’in en önemli özelliği, runtime sağlamlığını garanti eden type checker’ıdır. Porffor çok ilgi çekici; bir süredir takip ediyorum ve başarılı olmasını diliyorum.
    • Porffor katkıcısı olarak bunun iyi bir karşılaştırma olduğunu düşünüyorum. Ancak Porffor teknik olarak promise desteğine de sahip. Sadece senkron çalışıyor.
      Kiesel’e benzer bir yaklaşım: https://kiesel.dev/
    • Birkaç küçük düzeltme var. Porffor henüz tamamen self-hosting değil, ama bunun mümkün olmasını bekliyoruz. Yine de Array.prototype.filter, Math.sin, atob gibi built-in işlevler kısmen kendisini derliyor.
      Son zamanlarda Porffor temel düzeyde async/promise/await desteği de sunmaya başladı. Henüz çok iyi çalışmıyor.
    • LLVM’e bağımlı olmayı kötü bir şeymiş gibi ifade etmişsin.
  • JavaScript’in kolay derlenebilen bir alt kümesi var; zor olan kısım onun dışındaki uzun kuyruk. Yine de bu sınırın tam olarak nerede olduğuna ve o alt kümeden ne kadar fayda sağlanabileceğine dair araştırmaların yapılması harika.

  • String.blink desteklenmesini içtenlikle seviyorum. Geliştiricinin mizah ve oyunculuk duygusuna sahip olması her zaman iyi bir işarettir.

    • Eğer amaç “ECMAScript host’unun bir web tarayıcısı gibi davranması” ise, bunu elbette desteklemek gerekir. Çünkü spesifikasyonun bir parçası: https://tc39.es/ecma262/multipage/additional-ecmascript-feat...
      Uygulaması da function() { return "" + this + ""; } düzeyinde önemsiz olduğu için, ECMAScript host’u web tarayıcısı olmasa bile uygulanabilir. Böyle durumlarda bu isteğe bağlıdır. Bunun “mizah ya da oyunculuk” ile ilgili olmasını beklemezdim.
    • String.blink, test262 içinde yer aldığı için proje hedefine ulaşmak istiyorsa fiilen bunu desteklemesi gerekiyor.
  • Gözden kaçırdığım ince farkın ne olduğunu merak ediyorum. Neden “ahead-of-time derlenen JS motoru”, “JS-to-Wasm derleyicisi”nden daha iyi bir tanım olsun, emin değilim. Bu daha çok bir çerçeveleme stratejisiyse, o da kabul edilebilir.

    • Zaten JS interpreter’ı bundle edip JS-to-WASM yapan projeler var. Dolayısıyla bunun, o tür yaklaşımlardan farkı daha net vurgulamak için seçilmiş bir ifade olması muhtemel.
  • Burada açıklanan sürümleme şeması biraz şüpheli görünüyor.
    Bazı değişiklikler yüzünden kimi Test262 testlerinde gerileme yaşanırsa sürüm numarası da birlikte geri gidebilir. Yani Porffor aynı anda hem tek yönlü artan sürüm numaralarına hem de gerekli değişikliklerin Test262 gerilemesine yol açabilmesine sahip olamaz.
    https://github.com/CanadaHonk/porffor?tab=readme-ov-file#ver...

    • Muhtemelen amaç, Test262 gerilemesine neden olan çalışmaların geçici olarak ayrı bir branch’te yapılması ve ancak gerilemeyi ortadan kaldıracak düzeltmeler de tamamlandıktan sonra main’e merge edilmesidir. Yeni sürüm numarası da yalnızca bu merge gerçekleştikten sonra kullanılabilir.
  • Galce’de “mor” anlamına geliyor.

    • Kökeni mor rengini ifade eden Yunanca sözcüğe dayanıyor; aynı kökten gelen İngilizce kelimeler arasında muhtemelen en yaygın olanı, mor renkli bir mineral olan porphyry.
  • Farklı kullanım amaçlarına sahip birden fazla JS motorunun ortaya çıkmasını görmek ferahlatıcı.
    Uygulamalara eklenti gömmek için quickjs’e, llrt üzerinden daha fazla Node uyumlu API ekleme işi üzerinde çalışıyordum.
    https://github.com/awslabs/llrt