deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

Node.js 26.9, node:ffi'yi varsayılan olarak etkinleştirdi, artık experimental bayrağı gerekmiyor

Node.js 26.9.0, node:ffi modülünü varsayılan olarak etkin biçimde sunuyor; böylece JavaScript, derlenmiş addon'lara gerek duymadan yerel C kütüphanelerini çağırabiliyor. İlk benchmark'lar, çağrı başına ek yükte N-API addon'larının biraz gerisinde kaldığını gösteriyor.

Node.js 26.9, node:ffi'yi varsayılan olarak etkinleştirdi, artık experimental bayrağı gerekmiyor

16 Eylül'de yayımlanan Node.js 26.9.0, sonuçları çok büyük olan tek satırlık bir changelog girdisi taşıyor: derlenmiş bir addon yazmadan yerel C fonksiyonlarını çağırmaya yarayan yerleşik modül olan node:ffi artık herhangi bir başlatma bayrağı olmadan kullanılabiliyor. dev.to'da yayımlanan uygulamalı bir inceleme, bu değişikliğin pratikte ne anlama geldiğini ele aldı; kısa versiyonu şu: Node'da "bir C kütüphanesini çağırmak" artık daha kolay — ama beraberinde bazı tehlikeli noktalar da getirdi.

Bayrağın kaldırılması

26.8.2'ye kadar node:ffi'ye dokunmak için Node'un --experimental-ffi ile başlatılması gerekiyordu. 26.9.0'da modül hiçbir bayrak olmadan yükleniyor; yine de API'nin değişebileceğini belirten bir ExperimentalWarning basıyor. Eski etkinleştirme bayrağı artık gereksiz ve önceki davranışa dönüşün tek yolu --no-experimental-ffi; bu bayrakla node:ffi'yi içe aktarmak "unknown built-in module" hatasıyla başarısız oluyor.

Bu, sürüm sabitlemeleri açısından pratik bir sonuç doğuruyor: node:ffi'yi içe aktaran kod, bayrak olmayan kurulumlarda eskiden gürültülü biçimde başarısız oluyordu; 26.9.0'da ise sorunsuz çalışıyor. CI'da katı Node sürüm aralıkları kullanan herkes, hata modunun altında değiştiğinin farkında olmalı.

Bir çağrının maliyeti

dev.to benchmark'larına göre — trivial bir add fonksiyonuna beş milyon çağrı zamanlanmış — düz JavaScript çağrı başına yaklaşık 2.2 ile 3.0 nanosaniye, eşdeğer bir N-API addon'u 34.1 ile 35.8, node:ffi ise 37.5 ile 38.1 nanosaniye sürdü. FFI, derlenmiş addon'un 7 ila 8 puan gerisinde kalıyor ve ikisi de JavaScript'te kalmanın yaklaşık on beş katı maliyette — bunun nedeni, kontrol her JS/yerel sınırını geçtiğinde değerlerin dönüştürülmesinin getirdiği yük.

Çıkarılacak ders, FFI'nin mutlak anlamda yavaş olması değil, hız açısından yerini alması gereken şeyi geçememesi. Asıl argümanı kullanım kolaylığı: node-gyp yok, deploy imajlarına gömülü derleyici araç zinciri yok ve Node'un ABI'si değiştiğinde yeniden derleme yok.

FFI'nın gerçekten kazandığı yerler

Çağrı başına ek yük, trivial fonksiyonlarda baskın olduğundan, aynı inceleme karşılaştırmayı toplu bir işlem olarak tekrarladı: on milyon float64 değerini, yerel fonksiyona alttaki buffer'ın pointer'ını vererek toplamak. FFI, işi 13.8 ile 14.0 milisaniyede tamamladı; düz bir JavaScript döngüsü ise 16.0 ile 18.4 milisaniye sürdü — yüzde 15 ila 20'lik bir avantaj; çünkü tek bir çağrı tüm veriyi taşıyor ve sabit ek yük milyonlarca çağrıya yayılmıyor.

Diğer uçta, C'de hesaplanan tek bir fib(75) 0.09 milisaniye sürdü, JavaScript'te ise 0.10 milisaniye — fiilen beraberlik; çünkü V8'nin JIT'i basit bir döngüyü gcc -O2 kadar iyi derliyor. FFI'nın sabit maliyeti yalnızca yüksek çağrı sayılarında ya da büyük veri hacimlerinde kendini amorti ediyor; yerel koda tek seferlik bir atlamak genellikle bu durum değil.

Neyi doğruluyor, neyi doğrulamıyor

Node'un dokümantasyonu modülü unsafe olarak etiketliyor ve dev.to testleri, korumanın seçici olduğunu ortaya koydu. Argüman biçimi kontrol ediliyor: uint64 beklenen yere Number geçmek, zorlamak yerine BigInt isteyen bir TypeError fırlatıyor ve iki argümanlı bir fonksiyonun tek argümanla çağrılması reddediliyor. Ancak int64 döndüren bir fonksiyonu int32 döndürüyormuş gibi bildirmek, hata veya uyarı olmadan sessizce kırpılmış yanlış bir değer üretti — gözden geçirmede fark edilmeyen türden bir hata; çünkü görünümünde yanlış olan hiçbir şey yok. Daha kötüsü, bir fonksiyona geçerli bir pointer'ı şişirilmiş bir uzunlukla birlikte vermek, 139 çıkış koduyla anında bir segfault'a yol açtı — bir buffer boyut değiştirdiğinde ama çağrı noktası değişmediğinde bir refactor'ün yaratabileceği türden bir tutarsızlık. Kısacası: sayılar ve skaler tipler doğrulanıyor; pointer sınırları ve dönüş tipi bildirimleri doğrulanmıyor.

İzinler ve eşzamanlılık

Permission Model, FFI'yı ayrı biçimde kontrol ediyor. --permission altında çağrılar --allow-ffi verilene kadar reddediliyor ve Node, bu bayrağın permission modelini geçersiz kılabileceği gerekçesiyle son derece dikkatli kullanılması gerektiğine dair bir güvenlik uyarısı ekliyor — bir kez yüklenen yerel kod, diğer kısıtlamaların hiçbirine tabi değil. Kapatılmış kütüphane handle'ları "Library is closed" hatasıyla temiz biçimde başarısız oluyor ve daha yeni olan using sözdizimi bunları kapsam çıkışında otomatik olarak kapatıyor. FFI çağrıları ayrıca özel bir kurulum olmadan worker thread'lerden de çalıştı; verimlilik tek worker'da saniyede yaklaşık 7.1 milyon çağrıdan dört worker'da yaklaşık 23 milyona ölçeklendi — bu da çağrıların global bir kilit üzerinden sıralanmak yerine paralelleştiğini gösteriyor.

Neden önemli

Varsayılan açık FFI, Node'dan yerel kod çağırmanın en büyük lojistik engelini — build zincirini — ortadan kaldırıyor; bu da hem hafif deploy imajları hem de önceden derlenmiş binary'ler olmadan dağıtmak isteyen paketler için önemli. Ancak modül hâlâ experimental ve hata modları da kötü cinsten: exception yerine sessiz sayısal kırpılma ve segfault'lar, ayrıca Permission Model'i delme yeteneğinin dokümante edilmiş olması. Ekipler bunu, toplu ve iyi test edilmiş yerel çağrılar için bir kolaylık olarak görmeli — mevcut addon'lara kıyasla bir performans yükseltmesi değil ve güvenliğe duyarlı süreçlerde hafife alınarak etkinleştirilecek bir şey değil.

  • #node-js
  • #ffi
  • #native-modules
  • #javascript
  • #benchmarks

İlgili yazılar