İçeriğe atla

tsrs ve TypeScript 7: TypeScript Derleyicisinin Rust Portunu Ölçmek

TypeScript 7 derleyicisinin Rust portu tsrs'i tsgo 7.0.2, 7.1 beta ve tsc 5.9 ile dört projede karşılaştırdım: hız, bellek, thread kullanımı ve hata çıktısı.

Ayhan Sipahi Ayhan Sipahi

4 vCPU’lu bir makinede tsrs 0.18.0, dört projede tip kontrolünü TypeScript 7.1 beta’dan 3,9 ila 4,9 kat, TypeScript 7.0.2’den ise 4,8 ila 6,5 kat daha hızlı yaptı. En yüksek bellek kullanımı 7.1 beta’ya göre 2 ila 3 kat, 7.0.2’ye göre 3 ila 4,6 kat daha düşüktü. VS Code kod tabanında (10.427 dosya, 3,5 milyon satır) kontrol süresi TypeScript 7.0.2 ile 32 saniyeden 5,6 saniyeye indi. Hata çıktısı iki projede 7.1 beta ile birebir aynıydı. Diğer iki projede hata konumları ve kodları aynıydı, ancak birkaç mesaj tipleri farklı yazdırıyordu.

Bu sonuçlar tsrs’i şimdiden CI’da, tsc’nin yanında ikinci bir kontrol olarak denemeye değer kılıyor. Ama henüz tam bir yerine geçme anlamına gelmiyor. Yayınlanan sürüm, bir TypeScript geliştirme commit’inin 1.0 öncesi portu. Ayrıca bir sonraki sürümde varsayılan ayarlar değişiyor ve bu, “tsrs hızı” ifadesinin anlamını da değiştiriyor.

Bu yazıda tsrs’in ne olduğunu, nasıl ölçtüğümü, sonuçları ve hızlanmanın nereden geldiğini anlatıyorum. Tüm komutlar yazıda var, böylece sonuçlar başka donanımlarda da kontrol edilebilir.

tsrs Nedir#

tsrs (yeni sekmede açılır), TypeScript 7 derleyicisinin ve language server’ının Rust portu. Oxc projesi (yeni sekmede açılır) altında geliştiriliyor. Cloudflare ve VoidZero, projenin ilk yazarı Max Schwenk ile birlikte çalışıyor. Cloudflare duyurusu (yeni sekmede açılır) çalışmanın TypeScript ekibiyle yakın iş birliği içinde yapıldığını söylüyor ve açıkça “we are not forking TypeScript” diyor.

Önemli olan, neyin port edildiği. TypeScript 7’nin kendisi de bir port: Microsoft JavaScript derleyicisini Go ile yeniden yazdı ve TypeScript 7.0 (yeni sekmede açılır) bu Go derleyicisini normal typescript paketi olarak yayınladı. tsrs ise Go kodunu fonksiyon fonksiyon port ediyor. Uyumluluk sayfası (yeni sekmede açılır) bunu “The Go code is the spec” diye özetliyor. Hata mesajlarında, sıralarında, metinlerinde veya çıkış kodunda görülen her fark bir tsrs hatası sayılıyor.

pinned commit is the spec

TypeScript 5.x / 6.x JavaScript ile yazılmış derleyici

TypeScript 7 Go portu (tsgo)

tsrs Go kodunun Rust portu

Bu yüzden tsrs, TypeScript 5’in değil TypeScript 7’nin yerine geçecek şekilde tasarlanmış. TypeScript 7’nin kaldırdığı seçenekler tsrs’te de aynı şekilde reddediliyor. Bu sitenin kendi tsconfig.json dosyası iki derleyicide de aynı hatayla durdu:

tsconfig.json(13,5): error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.

Yani önce TypeScript 7 geçişi yapılmalı. tsrs, bundan sonra verilecek bir karar.

Nasıl Ölçtüm#

Kurulum, mümkün olduğunca tsrs benchmark metodolojisini (yeni sekmede açılır) izliyor. Aynı sabitlenmiş proje commit’leri ve aynı derleyici bayrakları kullanıldı, sadece ölçek daha küçük.

Makine. 4 vCPU (2,1 GHz Intel Xeon), 15 GB RAM ve Linux x86_64 olan bir bulut VM. Paylaşımlı bir VM, ayrılmış donanıma göre daha gürültülü sonuç verir. Bu yüzden hyperfine (yeni sekmede açılır) ile ölçülen medyan değerleri ve standart sapmayı esas aldım.

Derleyiciler.

DerleyiciPaketNot
tsrstsrs@0.18.0-ts7.1.0-dev.20260929Yayınlanmış son sürüm; TypeScript b85298b6 commit’ini (7.1 dev) port ediyor
tsgo 7.1 betatypescript@7.1.0-betatsrs’in port ettiği koda en yakın resmi sürüm
tsgo 7.0.2typescript@7.0.2Güncel kararlı TypeScript
tsc 5.9typescript@5.9Karşılaştırma için JavaScript derleyicisi

Native derleyicilerde platform binary’lerini doğrudan çağırdım. Böylece Node başlatıcısının süresi ölçüme girmedi.

Projeler. Üçü, tsrs benchmark kataloğundan, sabitlenmiş commit’leriyle alındı. Dördüncüsü bu site.

ProjeDosyaSatırTip
sph.sh (Astro sitesi, bu blog)866246.45260.106
xstate1.536398.462373.803
webpack1.607656.348588.331
VS Code (src)10.4273.510.9442.866.525

Dosya ve satır sayılarına node_modules içindeki declaration dosyaları da dahil. Küçük bir sitenin 246 bin satıra ulaşmasının nedeni bu.

Komut. Ölçülen her çalıştırmada aynı bayraklar kullanıldı:

<compiler> -p <project> --noEmit --incremental false --pretty false

Her derleyici kendi varsayılan ayarlarıyla çalıştı. 4 vCPU’da bu, tüm native derleyiciler için 4 checker thread’i demek: tsgo her zaman 4 kullanıyor, tsrs 0.18 ise çekirdek başına bir tane. Yani thread sayısı eşit. Cloudflare yazısının da önerdiği karşılaştırma bu. İkinci turda hepsi --singleThreaded ile çalıştı.

En yüksek bellek kullanımı, GNU time ile ölçülen maksimum resident set size değeri. Üç çalıştırmanın medyanı alındı.

Sonuçlar: Varsayılan Ayarlar#

Süre (medyan; 10 çalıştırma, VS Code’da 5; tsc 5.9 için 2-3 çalıştırma, VS Code’da 1):

Projetsc 5.9tsgo 7.0.2tsgo 7.1 betatsrs 0.18tsrs / 7.1 betatsrs / 7.0.2
sph.sh3,71 sn0,63 sn0,52 sn0,13 sn3,9x4,8x
xstate13,86 sn2,48 sn1,92 sn0,39 sn4,9x6,3x
webpack22,23 sn4,18 sn3,16 sn0,65 sn4,9x6,5x
VS Code252 sn31,99 sn22,38 sn5,59 sn4,0x5,7x

En yüksek bellek (3 çalıştırmanın medyanı; tsc 5.9 tek çalıştırma):

Projetsc 5.9tsgo 7.0.2tsgo 7.1 betatsrs 0.18tsrs / 7.1 betatsrs / 7.0.2
sph.sh390 MiB249 MiB194 MiB96 MiB2,0x az2,6x az
xstate850 MiB752 MiB578 MiB224 MiB2,6x az3,4x az
webpack1.294 MiB1.258 MiB862 MiB375 MiB2,3x az3,4x az
VS Code8.518 MiB8.210 MiB5.542 MiB1.801 MiB3,1x az4,6x az

Bazı gözlemler:

  • Mutlak süre açısından en büyük adım hâlâ tsc 5.9’dan TypeScript 7’ye geçiş. VS Code’da bu geçiş 220 saniye kazandırıyor. tsrs bunun üstüne 17 ila 26 saniye daha kazandırıyor. Bu site gibi küçük bir projede tüm native seçenekler bir saniyenin altında bitiyor.
  • TypeScript 7.1 beta burada 7.0.2’den 1,2 ila 1,4 kat daha hızlı. Cloudflare yazısına göre bu optimizasyonların bir kısmı tsrs’ten geliyor. tsrs’i sadece 7.0.2 ile karşılaştırmak, güncel TypeScript’e göre farkı olduğundan büyük gösterir.
  • CI’da bellek, hızdan daha önemli olabilir. VS Code büyüklüğünde bir kontrol 7.0.2 ile yaklaşık 8 GB, tsrs ile 1,8 GB bellek istiyor. Kontrolün standart bir runner’a sığıp sığmayacağını bu belirliyor.

Sonuçlar: Tek Thread#

--singleThreaded ile her derleyici aynı işi tek çekirdekte yapıyor. Bu, kodun verimliliğini thread kullanımından ayırıyor.

Projetsgo 7.0.2tsgo 7.1 betatsrs 0.18tsrs / 7.1 beta
sph.sh0,77 sn0,67 sn0,27 sn2,5x
xstate3,69 sn3,03 sn1,14 sn2,7x
webpack6,14 sn4,90 sn1,97 sn2,5x
VS Code57,77 sn42,87 sn18,90 sn2,3x

Tek çekirdekte tsrs, 7.1 beta’dan yaklaşık 2,5 kat hızlı. VS Code’da tek thread’li tsrs (18,9 sn), dört thread’li tsgo 7.0.2’yi (32 sn) de geçiyor.

Hızlanma Nereden Geliyor#

İki tablo karşılaştırıldığında iki ayrı etki görülüyor. Aşağıdaki paralel hızlanma, tek thread süresinin varsayılan (4 thread) süreye bölümü:

Projetsgo 7.0.2tsgo 7.1 betatsrs 0.18
sph.sh1,2x1,3x2,0x
xstate1,5x1,6x2,9x
webpack1,5x1,6x3,0x
VS Code1,8x1,9x3,4x

Yani 4 çekirdekteki 4-5 katlık fark kabaca iki parçaya ayrılıyor:

  1. Yaklaşık 2,5 kat, çekirdek başına verimlilikten. Aynı algoritma Go yerine Rust ile çalışıyor. tsrs Rust notları (yeni sekmede açılır) garbage collector yerine thread başına arena’ları, sıkıştırılmış veri yerleşimlerini, mimalloc’u ve profile-guided optimization’ı sayıyor. Ben profil çıkarmadım. Bu yüzden bunu ölçülmüş bir döküm değil, projenin kendi açıklaması olarak okumak gerekir.
  2. Yaklaşık 1,6 ila 1,9 kat, 4 thread’in daha iyi kullanımından. tsgo, 4 checker ve sabit bir dosya dağılımıyla kontrol yapıyor. 4 çekirdekten 2 katın altında verim alıyor. tsrs 0.18 ise varsayılan olarak locality scheduling, work stealing ve lazy type member özelliklerini ekliyor. Büyük projelerde 3 ila 3,4 kat verim alıyor.

Bir sonraki sürümde değişen, bu ikinci parça.

Varsayılan Davranış Değişiyor#

0.18.0’da tsrs optimizasyonlarını varsayılan olarak çalıştırıyor. 0.19.0 beta adayı bunu tersine çeviriyor. Sürüm notları (yeni sekmede açılır) yeni varsayılanı “the pinned TypeScript defaults: four checkers, Go’s file assignment and checker history, and eager members” olarak tanımlıyor. Scheduling ve bellek optimizasyonları, isteğe bağlı --fast bayrağının veya TSRS_FAST=1 değişkeninin arkasına taşınıyor.

README (yeni sekmede açılır), fast modun ödünleşimlerini sıralıyor:

  • Kontrol sırası, bilinen recursive type ve instantiation limit durumlarında hangi hataların çıkacağını değiştirebilir. Çıkış kodu da değişebilir.
  • Fast mod daha fazla CPU, daha fazla checker ile de daha fazla bellek kullanabilir.
  • Tip ve instantiation sayaçları TypeScript’inkilerden farklı olabilir.

Bu mantıklı bir ayrım. Varsayılan mod birebir uyumluluğu, fast mod ise hızı hedefliyor. Ama bu, yukarıdaki sonuçların 0.19’un varsayılan moduna değil, --fast moduna daha yakın olduğu anlamına geliyor. 0.19’un varsayılan modunun tek thread oranıyla 4 thread oranı arasında bir yere düşmesini beklerim, ama yayınlanmamış bir sürümü ölçmedim. Güncellemeden sonra kendi projenizde ölçmek en doğrusu.

Manşet rakamlar hakkında bir not daha. Upstream benchmark sonuçları (yeni sekmede açılır) tsgo 7.0.2’ye göre 16,9 kata varan hızlanma gösteriyor. Bu ölçümler 16 vCPU’da yapıldı: tsrs 16 checker, tsgo ise varsayılan 4 checker ile çalıştı. Büyük donanımda “ikisi de varsayılan ayarlarda” karşılaştırması olarak bu adil. Ama 4 çekirdekli bir CI runner’ında ikisi de 4 checker kullanıyor ve fark küçülüyor. Makinenin çekirdek sayısı, sonucu derleyici kadar etkiliyor.

Hata Çıktısı: Aynı Hatalar, Bazı Farklı Metinler#

Hata çıktısı farklıysa hızın pek anlamı kalmaz. Her projede tsrs 0.18 ile tsgo 7.1 beta’nın tüm hata çıktısını karşılaştırdım:

ProjeHata sayısı (tsrs / 7.1 beta / 7.0.2)7.1 beta ile karşılaştırma
sph.sh79 / 79 / 79Aynı hatalar; bir açıklama satırı bir tipi farklı yazdırıyor
xstate0 / 0 / 0Byte düzeyinde aynı
webpack840 / 840 / 848840 hatanın hepsinde dosya, satır, sütun ve kod aynı; 6 mesaj tipleri farklı yazdırıyor
VS Code371 / 371 / 359Byte düzeyinde aynı

webpack’teki farklar tip yazdırmada. plugins: ModuleFederationPlugin[] gibi uzun bir tip, farklı bir noktada plugins: ... olarak kısaltılıyor. Bazı iç içe açıklama satırları da bir çıktıda var, diğerinde yok. tsgo 7.1 beta ve tsrs, tekrarlanan çalıştırmalarda kendi içinde hep aynı çıktıyı verdi. tsrs 0.18, 7.1 beta etiketini değil, bir 7.1 dev commit’ini port ediyor. Bu farkın bir kısmı o sürüm farkından gelebilir. Bu ölçümlerle iki nedeni birbirinden ayıramıyorum.

7.0.2 ile hata sayıları daha fazla farklı (webpack’te 848’e karşı 840, VS Code’da 359’a karşı 371). tsrs sonuç sayfasına göre, tsrs’in port ettiği commit’ten derlenen typescript@7.1.0-dev.20260930.4 tam olarak tsrs’in hatalarını veriyor. Yani bu fark tsrs’ten değil, TypeScript sürümleri arasındaki değişikliklerden geliyor.

Çoğu ekip için pratik risk dar. Hata mesajı metnini birebir karşılaştıran her şey, örneğin derleyici çıktısının snapshot testleri, bozulabilir. Sadece geçti/kaldı sonucuna bakan bir tip kontrolü çok daha az etkilenir.

Bu Sonuçlarla Ne Yapardım#

Hayir

Evet

Hayir

Evet

Proje TypeScript 7'de mi?

Önce TS 7'ye geçin (baseUrl, kaldırılan seçenekler)

Tip kontrolü CI'da yavaş mı ya da bellek sınırında mı?

tsc ile devam edin; kazanç bir saniyenin altında

tsrs'i engellemeyen bir CI job'ı olarak ekleyin

Çıkış kodlarını ve hata konumlarını tsc ile karşılaştırın

Uyum birkaç sürüm boyunca korunursa ana kontrol yapın

  • Küçük projeler. Bu site TypeScript 7.1 beta ile 0,5 saniyede kontrol ediliyor. 0,4 saniye kazanmak, ikinci bir derleyiciyi gerekçelendirmiyor.
  • Büyük projeler ve monorepo’lar. Burada tsrs CI tablosunu değiştiriyor. VS Code ölçeğindeki kontroller 22-32 saniyeden yaklaşık 6 saniyeye, 5-8 GB bellekten 2 GB’ın altına iniyor. Daha küçük runner’lar seçenek hâline geliyor.
  • Nasıl benimsenir. tsrs’i önce ayrı ve engellemeyen bir job olarak çalıştırın, tsc’yi ana kontrol olarak tutun. Varsayılanlar sürümler arasında değiştiği için tsrs sürümünü tam olarak sabitleyin. Kendi kodunuzda çıkış kodlarının ve hata konumlarının aynı olduğunu kontrol edin. Bu birkaç sürüm boyunca korunursa ana kontrole taşıyın.
  • Editörler. tsrs bir language server da içeriyor. Burada editör gecikmesini ölçmedim, bu yüzden günlük kullanımda nasıl karşılaştırıldığını söyleyemem.

Oxc araç zinciri linting ve formatting tarafında da aynı yolu izledi. Native araçlar önce mevcut aracın yanında uyumlu olduklarını kanıtlıyor, fark kapandıktan sonra onun yerini alıyor.

Benchmark’ı Tekrarlamak#

Kurulumun tamamı kısa bir script’e sığıyor. Node, git, hyperfine ve GNU time gerekiyor:

mkdir tsbench && cd tsbench
npm init -y >/dev/null
npm i tsrs@0.18.0-ts7.1.0-dev.20260929 \
  ts70@npm:typescript@7.0.2 ts71@npm:typescript@7.1.0-beta

TSRS=node_modules/@tsrs/linux-x64-gnu/tsrs
TSGO70=node_modules/@typescript/typescript-linux-x64/lib/tsc
TSGO71=node_modules/ts71/node_modules/@typescript/typescript-linux-x64/lib/tsc

# tsrs bench/projects.json içinde sabitlenen commit
git clone https://github.com/webpack/webpack.git && cd webpack
git checkout b54c5e2aa26dd0267bc904d45402f811f100186c
npx yarn@1.22.22 install --ignore-scripts --ignore-engines
npx yarn@1.22.22 link && npx yarn@1.22.22 link webpack

ARGS="-p . --noEmit --incremental false --pretty false"
# -i: tip hataları 1 veya 2 çıkış koduyla biter, burada bu beklenen durum
hyperfine -N -i --warmup 1 --runs 10 \
  -n tsrs "../$TSRS $ARGS" \
  -n tsgo-7.0.2 "../$TSGO70 $ARGS" \
  -n tsgo-7.1-beta "../$TSGO71 $ARGS"

# En yüksek bellek (KB cinsinden max RSS)
/usr/bin/time -f "%M KB" "../$TSRS" $ARGS > /dev/null

Platform paketlerinin yolları macOS’ta ve musl Linux’ta farklı. Doğru binary için node_modules/@tsrs/ ve node_modules/@typescript/ klasörlerine bakın.

Sonuç#

tsrs ana iddiasını karşılıyor. Eşit thread sayısında, test ettiğim her projede TypeScript 7.1 beta’dan 4-5 kat hızlıydı ve 2-3 kat daha az bellek kullandı. Dört projenin ikisinde çıktı byte düzeyinde aynıydı. Bu kadar genç bir proje için bu kombinasyon alışılmadık. Bunun nedeni, Go derleyicisini spesifikasyon olarak kabul etmesi.

Sınırları da net. Proje 1.0 öncesinde, TypeScript geliştirme commit’lerini takip ediyor ve bir sonraki sürüm varsayılan modun davranışını değiştiriyor. Çok çekirdekli makinelerdeki hızlanmanın büyük kısmı, isteğe bağlı hâle gelen scheduling’den geliyor. Büyük kod tabanlarında şimdiden tsc’nin yanında çalıştırmaya değer. Küçük projelerde ise TypeScript 7 tek başına yeterli.

Kaynaklar#

İlgili yazılar