38 puan yazan doscm164 2025-09-16 | Henüz yorum yok. | WhatsApp'ta paylaş

> Bu yazı, V8 motoru v11.x temel alınarak yazıldı ve basit bir çöp toplayıcı tanıtımının ötesine geçerek V8’in saniyede milyonlarca işlev çağrısını ve GB düzeyindeki belleği nasıl verimli yönettiğini inceliyor.

Bellek yönetiminin özü: V8 mimarisini anlamak

JavaScript’in basit bir betik dilinden yüksek performanslı bir uygulama platformuna evrilebilmesi, V8’in yenilikçi bellek yönetimi sayesinde mümkün oldu. İlk dönem V8, onlarca milisaniyelik GC duraklamalarıyla kullanıcı deneyimine zarar veriyordu, ancak bugün bu süre birkaç milisaniye seviyesine indirildi. Bu devrim niteliğindeki değişimin başlangıç noktası, nesnelerin temsil edilme biçimidir.

Nesneleri temsil etmenin benzersiz yolu: Hidden Classes

V8, JavaScript nesnelerini dahili olarak HeapObject olarak temsil eder ve her nesne aşağıdaki yapıya sahiptir.

// V8 iç nesne yapısı (basitleştirilmiş)  
class HeapObject {  
  Map* map_;           // Hidden Class işaretçisi (4/8 bytes)  
  Properties* props_;  // dinamik özellik deposu  
  Elements* elements_; // dizi öğesi deposu  
  // ... satır içi özellikler  
};  

Hidden Classes (Maps), V8’in temel optimizasyon tekniklerinden biridir ve dinamik tipli bir dilde statik tipli diller seviyesinde performans elde edilmesini sağlar. Nesne yapısı her değiştiğinde yeni bir Hidden Class’a geçiş (transition) yapılır; bu da Inline Cache (IC) ile birleşerek özellik erişimini optimize eder.

Hidden Classes, dinamik tipli bir dil olan JavaScript’te statik tipli diller düzeyinde performans elde etmeyi sağlayan temel teknolojidir. Ancak bu kadar karmaşık nesne yapılarını verimli biçimde yönetmek için gelişmiş bir bellek yönetimi stratejisi gerekir.

Gerçekçi zorluk: Bellek yönetimi neden zordur?

Modern web uygulamaları büyük miktarda heap belleği kullanır ve 60FPS animasyonlar ile gerçek zamanlı etkileşim talep eder. V8’in GC’si aşağıdaki zorlukları çözmek zorundadır.

  1. Latency vs Throughput ödünleşimi: GC pause time’ı en aza indirirken yeterli bellek geri kazanım oranını sağlamak
  2. Memory Fragmentation: Uzun süre çalışan SPA’larda bellek parçalanmasını önlemek
  3. Cross-heap References: JavaScript ile WebAssembly arasındaki karşılıklı referansları verimli biçimde yönetmek
  4. Incremental/Concurrent işleme: Ana iş parçacığını bloklamadan GC gerçekleştirmek

Özellikle Chrome’un Site Isolation mimarisinde her iframe ayrı bir V8 isolate’a sahip olduğundan, bellek verimliliği daha da önemli hale geldi. Bu zorlukları çözmek için V8, nesil bazlı heap yapısı şeklinde yenilikçi bir yaklaşım benimsedi.

Temel strateji: Nesil bazlı heap yapısının tasarımı

Nesil bazlı heap yapısı ve bellek tahsis stratejisi

V8’in heap’i, basit bir Young/Old ayrımının ötesinde karmaşık bir katmanlı yapıya sahiptir.

V8 Heap (toplam boyut: nn MB ~ n GB)  
├── Young Generation (1-32MB)  
│   ├── Nursery (Semi-space 1)  
│   ├── Intermediate (Semi-space 2)  
│   └── Survivor Space  
├── Old Generation  
│   ├── Old Object Space  
│   ├── Code Space (çalıştırılabilir kod)  
│   ├── Map Space (Hidden Classes)  
│   └── Large Object Space (>256KB nesneler)  
└── Non-movable Spaces  
    ├── Read-only Space  
    └── Shared Space (cross-isolate)  

Bu katmanlı yapı, nesnelerin ömrüne göre optimize edilmiş işleme olanak tanır. TLAB (Thread-Local Allocation Buffer) tekniği sayesinde her iş parçacığı bağımsız bir tahsis tamponuna sahip olur; bu da eşzamanlılık çekişmesini en aza indirir. Tahsis, bump pointer yöntemiyle O(1) zamanda gerçekleştirilir.

Ancak nesil bazlı heap yapısı tek bir varsayıma dayanır.

Nesil bazlı nesne terfi (Promotion) mekanizması

V8’in nesne terfi mekanizması, yalnızca age tabanlı değil, bileşik sezgisel yöntemler kullanır.

  1. Age-based Promotion: 2 veya daha fazla Scavenge’ı atlatan nesneler
  2. Size-based Promotion: To-space %25’ten fazla dolarsa anında terfi
  3. Pretenuring: Tahsis bölgesi geri bildirimiyle baştan Old Space’e tahsis
// Pretenuring örneği - V8 deseni öğrenir  
function createLargeObject() {  
  return new Array(1000000); // birkaç kez çağrıldığında doğrudan Old Space’e tahsis  
}  

Write Barrier, nesiller arası referansları izler. Old -> Young referansı oluştuğunda remembered set’e kaydedilir ve Minor GC sırasında kök olarak işlenir.

// Write Barrier (basitleştirilmiş)  
if (is_old_object(obj) && is_young_object(value)) {  
  remembered_set.insert(obj_address);  
}  

[IMG] v8

Nesil hipotezinin doğrulanması: Weak Generational Hypothesis

V8 ekibinin ölçüm verilerine göre

  • Nesnelerin %95’i ilk Scavenge sırasında yok olur
  • Yalnızca %2’si Old Generation’a terfi eder
  • Young Generation GC 10-50ms, Old Generation GC ise 100-1000ms sürer

Bu istatistikler, nesil bazlı GC’nin neden etkili olduğunu açıklar. Ancak React gibi SPA framework’lerinde bu varsayım tamamen bozulur.

React ve V8 GC çatışması: Gerçek sorunlar

1. Fiber mimarisinin bellek deseni

React 16 ile birlikte tanıtılan Fiber mimarisi, V8’in nesil bazlı hipoteziyle doğrudan çatışıyor.

// React Fiber düğüm yapısı (simplified)  
class FiberNode {  
  constructor(element) {  
    this.type = element.type;  
    this.key = element.key;  
    this.props = element.props;  
    
    // Bu referanslar sorunun merkezinde  
    this.child = null;      // çocuk Fiber  
    this.sibling = null;    // kardeş Fiber  
    this.return = null;     // ebeveyn Fiber  
    this.alternate = null;  // önceki render’ın Fiber’ı (double buffering)  
    
    // Uzun ömürlü referanslar  
    this.memoizedState = null;     // Hooks durumu  
    this.memoizedProps = null;     // önceki props  
    this.updateQueue = null;        // güncelleme kuyruğu  
  }  
}  
  
// Gerçek bir React uygulamasındaki Fiber ağacı  
const fiberRoot = {  
  current: rootFiber,        // mevcut ağaç (Old Generation’a terfi eder)  
  workInProgress: null,      // üzerinde çalışılan ağaç (Young Generation)  
  pendingTime: 0,  
  finishedWork: null  
};  

Sorunlar

  • Fiber düğümleri, bileşen mount edildiği sürece yaşamaya devam eder
  • Her render’da alternate Fiber oluşturulur/korunur (double buffering)
  • Tüm ağaç Old Generation’a terfi ederek Major GC yükünü artırır
2. React Hooks ve closure bellek sızıntıları
// Yaygın bellek sızıntısı deseni  
function ExpensiveComponent() {  
  const [data, setData] = useState([]);  
  
  useEffect(() => {  
    // Bu closure tüm component scope'unu yakalar  
    const timer = setInterval(() => {  
      setData(prev => [...prev, generateLargeObject()]);  
    }, 1000);  
    
    // cleanup fonksiyonu unutulursa bellek sızıntısı olur  
    return () => clearInterval(timer);  
  }, []); // deps boş olsa da closure oluşturulur  
  
  // Her render'da yeni fonksiyon oluşturulur (Young Generation baskısı)  
  const handleClick = useCallback(() => {  
    // Bu fonksiyon data'nın tamamını closure içinde yakalar  
    console.log(data.length);  
  }, [data]);  
}  
  
// V8'in optimize etmekte zorlandığı Hook deseni  
function useComplexState() {  
  const [state, setState] = useState(() => {  
    // Bu başlatma fonksiyonu yalnızca bir kez çalışır  
    // ancak V8 bunu öngörmekte zorlanır  
    return createExpensiveInitialState();  
  });  
  
  // Hook'un linked list yapısı GC üzerinde yük oluşturur  
  const hook = {  
    memoizedState: state,  
    queue: updateQueue,  
    next: nextHook  // Sonraki Hook referansı  
  };  
}  
3. Virtual DOM ve Reconciliation'ın bellek ek yükü
// Virtual DOM nesnesi oluşturma deseni  
function createElement(type, props, ...children) {  
  return {  
    $$typeof: REACT_ELEMENT_TYPE,  
    type,  
    key: props?.key || null,  
    ref: props?.ref || null,  
    props: { ...props, children },  
    _owner: currentOwner  // Fiber referansı  
  };  
}  
  
// Her render'da oluşturulan geçici nesneler  
function render() {  
  // Bu nesnelerin tamamı Young Generation'da oluşturulur  
  return (  
    <div className="container">  
      {items.map(item => (  
        <Item   
          key={item.id}  
          data={item}  
          onClick={() => handleClick(item.id)}  
        />  
      ))}  
    </div>  
  );  
  // Reconciliation sonrasında çoğu hemen atılır  
}  
  
// Reconciliation sırasında oluşturulan iş nesneleri  
const updatePayload = {  
  type: 'UPDATE',  
  fiber: currentFiber,  
  partialState: newState,  
  callback: commitCallback,  
  next: null  // Update queue'nun linked list'i  
};  
4. React DevTools ve bellek profilleme
// React DevTools'un eklediği bellek ek yükü  
if (__DEV__) {  
  // Her Fiber'a hata ayıklama bilgisi ekle  
  fiber._debugSource = element._source;  
  fiber._debugOwner = element._owner;  
  fiber._debugHookTypes = hookTypes;  
  
  // Profilleme için zamanlama bilgisi  
  fiber.actualDuration = 0;  
  fiber.actualStartTime = 0;  
  fiber.selfBaseDuration = 0;  
  fiber.treeBaseDuration = 0;  
}  
  
// Bellek profilleme optimizasyon stratejisi  
class MemoryOptimizedComponent extends React.Component {  
  shouldComponentUpdate(nextProps) {  
    // Gereksiz render'ları önleyerek Virtual DOM oluşturmayı azalt  
    return !shallowEqual(this.props, nextProps);  
  }  
  
  componentDidMount() {  
    // WeakMap kullanarak GC dostu önbellekleme  
    this.cache = new WeakMap();  
  }  
  
  componentWillUnmount() {  
    // Açık temizleme ile bellek sızıntısını önle  
    this.cache = null;  
    this.subscription?.unsubscribe();  
  }  
}  
5. React 18'in Concurrent Features'ı ve GC optimizasyonu
// React 18'in Automatic Batching özelliği  
function handleMultipleUpdates() {  
  // Önceden: her setState ayrı bir render'ı tetiklerdi  
  // Şimdi: otomatik olarak batch edilerek GC yükü azaltılır  
  setCount(c => c + 1);  
  setFlag(f => !f);  
  setItems(i => [...i, newItem]);  
}  
  
// Suspense ve bellek yönetimi  
const LazyComponent = React.lazy(() => {  
  // Dinamik import ile başlangıç bellek kullanımı azaltılır  
  return import('./HeavyComponent');  
});  
  
// useDeferredValue ile öncelik tabanlı render  
function SearchResults({ query }) {  
  const deferredQuery = useDeferredValue(query);  
  
  // Acil olmayan güncellemeler ertelenerek işlenir  
  // Young Generation yükü dağıtılır  
  return <ExpensiveList query={deferredQuery} />;  
}  
6. Gerçek üretim optimizasyonu örnekleri
// Facebook'ta kullanılan bellek optimizasyon deseni  
const RecyclerListView = {  
  // Nesne havuzu ile GC yükünü azalt  
  viewPool: [],  
  
  getView() {  
    return this.viewPool.pop() || this.createView();  
  },  
  
  releaseView(view) {  
    view.reset();  
    this.viewPool.push(view);  
  }  
};  
  
// Relay'in GC dostu önbellek stratejisi  
class RelayCache {  
  constructor() {  
    // WeakMap ile otomatik bellek yönetimi  
    this.records = new WeakMap();  
    
    // TTL tabanlı sona erme ile Old Generation artışını önle  
    this.ttl = 5 * 60 * 1000; // 5 dakika  
  }  
  
  gc() {  
    // Eski kayıtları periyodik olarak temizle  
    const now = Date.now();  
    for (const [key, record] of this.records) {  
      if (now - record.fetchTime > this.ttl) {  
        this.records.delete(key);  
      }  
    }  
  }  
}  

React'teki bu bellek desenleri, V8 ekibinin temel varsayımlarıyla çelişse de V8 ekibi ile React ekibinin sürekli iş birliği sayesinde optimizasyonlar gerçekleştiriliyor. Özellikle React 18'in Concurrent Features'ı, V8'in Incremental GC'siyle iyi uyumlu çalışacak şekilde tasarlandı. Kaynak

Sorundan çözüme: GC algoritmalarının evrimi

Nesillere göre ayrılmış heap yapısı tek başına yeterli değildir. Çöp toplanırken uygulamayı durdurmadan bunu nasıl yapmak mümkün olabilir? V8’in tarihi, bu soruya yanıt arama süreciydi.

Başlangıç noktası: basit algoritmaların sınırları

2008’deki ilk V8, tipik bir Copy Algorithm olan Cheney's Algorithm tabanlı bir Semi-space toplayıcı kullanıyordu.

// Cheney Algorithm 의 Pseudocode  
void scavenge() {  
  scan = next = to_space.bottom;  
  // 1. 루트 스캐닝  
  for (root in roots) {  
    *root = copy(*root);  
  }  
  // 2. 너비 우선 탐색  
  while (scan < next) {  
    for (slot in slots_in(scan)) {  
      *slot = copy(*slot);  
    }  
    scan += object_size(scan);  
  }  
}  

Bu algoritma basit ve verimlidir, ancak modern web uygulamaları için ölümcül sorunlara sahiptir.

  • %50 bellek israfı: Semi-space’in doğasından gelen temel sınırlama
  • Cache Locality bozulması: BFS dolaşımı nedeniyle L1/L2 cache miss artışı
  • Tek iş parçacığı darboğazı: tüm işlerin yalnızca ana iş parçacığında yürütülmesi

Yeniliğin başlangıcı: Tri-color Marking’e geçiş

V8, artımlı işaretlemeyi gerçekleştirmek için Tri-color Marking algoritmasını devreye aldı.

// Tri-color invariant  
enum MarkColor {  
  WHITE = 0,  // 미방문, 회수 대상  
  GREY = 1,   // 방문했으나 자식 미처리  
  BLACK = 2   // 방문 완료, 살아있음  
};  
  
// 증분 마킹을 위한 Barrier   
void WriteBarrier(HeapObject* obj, Object** slot, Object* value) {  
  if (marking_state == INCREMENTAL &&  
      IsBlack(obj) && IsWhite(value)) {  
    // tri-color 위반  
    MarkGrey(value);  // 불변성 유지  
    marking_worklist.Push(value);  
  }  
}  

Bu yöntem, JavaScript çalışırken bile işaretlemenin kademeli olarak ilerlemesini sağlar. Ancak GC işini hâlâ ana iş parçacığının yapmak zorunda olması gibi temel sorun ortada kalıyordu. Bunu çözmek için V8 ekibi daha cesur bir adıma yöneldi.

Paradigma değişimi: Orinoco projesinin meydan okuması

Sadece Incremental GC yeterli değildi. Orinoco projesi, V8’in 2015’te başlayan büyük ölçekli GC yeniden yapılanmasıydı ve "Free the main thread(ana iş parçacığını özgürleştirmek)" gibi iddialı bir hedef ortaya koydu. Bunun için üç yenilikçi teknoloji sundu.

1. Paralel işleme (Parallel GC)

Paralel GC, birden fazla iş parçacığının aynı anda GC işi yürütmesini sağlar. V8, yük dengelemesini sağlamak için Work-Stealing algoritmasını kullanır.

class ParallelMarker {  
  std::atomic<Object*> marking_worklist;  
  std::atomic<size_t> bytes_marked;  
  
  void MarkInParallel() {  
    while (Object* obj = marking_worklist.pop()) {  
      MarkObject(obj);  
      // 로컬 작업 큐가 비어있을 때  
      if (local_worklist.empty()) {  
        StealFromOtherThread();  
      }  
    }  
  }  
};  

Ölçülen veri: 8 çekirdekli bir sistemde paralel işaretleme, tek iş parçacığına kıyasla 7.2 kat daha hızlı performans gösterdi. Ancak yalnızca paralelleştirme, uygulamayı durdurma ihtiyacını ortadan kaldırmaya yetmedi.

2. Artımlı işleme (Incremental Marking)

Artımlı işaretleme, GC işini birden çok aşamaya böler ve her aşamada yalnızca 5-10ms kullanır.

// 증분 단계 트리거링  
function shouldTriggerIncrementalStep() {  
  const allocated = bytesAllocatedSinceLastStep();  
  const threshold = heap.size() * 0.01; // 1% of heap  
  return allocated > threshold;  
}  
  
// 증분 단계마다 ~1MB를 처리  
function incrementalMarkingStep() {  
  const deadline = performance.now() + 5; // 5ms budget  
  while (performance.now() < deadline && !marking_worklist.empty()) {  
    markNextObject();  
  }  
}  

Marking Progress Bar: V8, işaretleme ilerleme oranını dahili olarak izleyerek ayırma hızı ile işaretleme hızını dengeler. Bu önemli bir ilerlemeydi, ancak temel çözüm eşzamanlı işlemede yatıyordu.

3. Eşzamanlı işleme (Concurrent Marking)

Eşzamanlı işaretleme en karmaşık ama aynı zamanda en etkili tekniktir. V8 burada Snapshot-at-the-Beginning (SATB) tekniğini kullanır.

class ConcurrentMarker {  
  void WriteBarrierSATB(HeapObject* obj, Object** slot, Object* new_value) {  
    Object* old_value = *slot;  
    if (concurrent_marking_active &&   
        IsWhite(old_value) && !IsWhite(new_value)) {  
      // SATB를 위해 이전 참조 보존  
      satb_buffer.push(old_value);  
    }  
    *slot = new_value;  
  }  
  
  void ConcurrentMarkingTask() {  
    // 헬퍼 스레드에서 실행  
    while (!marking_worklist.empty()) {  
      Object* obj = marking_worklist.pop();  
      // CAS를 사용한 lock-free 마킹  
      if (TryMarkBlack(obj)) {  
        VisitPointers(obj);  
      }  
    }  
  }  
};  

Performans etkisi: Eşzamanlı işaretleme, Major GC pause time’ı %60-70 azalttı.

Günümüzdeki V8: üç teknolojinin uyumu

Orinoco projesiyle geliştirilen bu üç teknoloji artık V8 GC’nin çekirdeğini oluşturuyor. Şimdi bunların her GC aşamasında nasıl birlikte çalıştığına bakalım.

Young Generation: paralel Scavenging

Young Generation GC, tamamen paralelleştirilmiştir. Ana iş parçacığı dursa da birden fazla yardımcı iş parçacığı aynı anda çalışır.

class ParallelScavenger {  
  void Scavenge() {  
    // 1. 루트 스캔을 병렬로 수행  
    parallel_for(roots, [](Root* root) {  
      EvacuateObject(root->object);  
    });  
    
    // 2. Work stealing으로 부하 균형  
    while (has_work() || can_steal_work()) {  
      Object* obj = get_next_object();  
      CopyToSurvivor(obj);  
    }  
    
    // 3. 포인터 업데이트도 병렬로  
    parallel_update_pointers();  
  }  
};  

Sonuç: 8 çekirdekli bir sistemde Young GC süresi 50ms’den 7ms’ye düştü

Old Generation: eşzamanlılığın en üst düzey kullanımı

Old Generation GC, eşzamanlılığı mümkün olan en yüksek düzeyde kullanır.

  1. Eşzamanlı işaretleme başlangıcı: JavaScript yürütülürken arka planda başlar
  2. Artımlı işaretleme: Ana thread periyodik olarak her seferinde 5 ms katkı sağlar
  3. Son temizlik: Kısa bir duraklamayla işaretleme tamamlanır (2-3 ms)
  4. Eşzamanlı süpürme: Bellek geri kazanımı yeniden arka planda yapılır
// Zaman çizelgesi örneği  
[JS 실행]-->[동시 마킹 시작]-->[JS 계속]-->[증분 5ms]-->[JS 계속]-->[최종 2ms]-->[JS 재개]  
    ↑            ↑             ↑           ↑  
할당 임계값 도달   백그라운드 작업   협력적 처리   최소 중단  

Idle-time GC: Idle Time zamanlaması

Tarayıcının Idle Time'ını kullanmak, V8'in önemli stratejilerinden biridir.

// Chrome'un requestIdleCallback'i ile entegrasyon  
requestIdleCallback((deadline) => {  
  // Kalan süreyi kontrol et  
  const timeRemaining = deadline.timeRemaining();  
  
  if (timeRemaining > 10) {  
    // Yeterli zaman varsa Major GC  
    triggerMajorGC();  
  } else if (timeRemaining > 2) {  
    // Süre kısaysa Minor GC  
    triggerMinorGC();  
  }  
});  

Bu üç tekniğin uyumlu biçimde birlikte çalışması, kullanıcının neredeyse hiç fark etmediği bir düzeyde GC yapılmasını mümkün kıldı. 60 FPS animasyonlar takılmadan çalışırken bellek de verimli şekilde yönetilir.

Derin inceleme: temel algoritmaların ayrıntılı uygulanışı

Şimdi V8 GC'nin temel algoritmalarının gerçekte nasıl uygulandığına daha yakından bakalım.

Concurrent Marking'in gelişmiş mekanizması

Eşzamanlı işaretlemenin özü, Tri-color Invariant'ı korumaktır.

class ConcurrentMarkingVisitor {  
  void VisitPointers(HeapObject* host, ObjectSlot start, ObjectSlot end) {  
    for (ObjectSlot slot = start; slot < end; ++slot) {  
      Object* target = *slot;  
      
      // 1. Zaten ziyaret edilmiş nesneyi atla  
      if (IsBlackOrGrey(target)) continue;  
      
      // 2. Eşzamanlılık güvenliği için CAS işlemi  
      if (CompareAndSwapColor(target, WHITE, GREY)) {  
        // 3. İş kuyruğuna ekle (lock-free queue)  
        marking_worklist_.Push(target);  
        
        // 4. Write barrier'ı etkinleştir  
        if (host->IsInOldSpace()) {  
          remembered_set_.Insert(slot);  
        }  
      }  
    }  
  }  
};  

Parallel Scavenger'ın iş dağıtım stratejisi

Paralel Scavenger, Dynamic Work Stealing kullanır.

class WorkStealingQueue {  
  bool TrySteal(Object** obj) {  
    // 1. Önce yerel kuyruğu kontrol et  
    if (local_queue_.Pop(obj)) return true;  
    
    // 2. Yerel kuyruk boşsa başka thread'lerden çal  
    for (int i = 0; i < num_threads; i++) {  
      if (global_queues_[i].TryStealHalf(&local_queue_)) {  
        return local_queue_.Pop(obj);  
      }  
    }  
    
    // 3. Tüm kuyruklar boşsa sonlandır  
    return false;  
  }  
};  

Bu algoritmaların gelişmiş uygulaması sayesinde V8, çok çekirdekli sistemlerin performansını en üst düzeyde kullanabilir.

Performans evriminin diğer ekseni: derleyici gelişimi

Sadece GC yeterli değildir. V8'in performans devrimi, derleyici ile GC'nin dengeli gelişiminden doğdu.

V8 derleyici pipeline'ının evrimi

1. nesil: Full-codegen + Crankshaft (2010-2016)

İlk dönem V8, iki aşamalı bir derleme stratejisi kullanıyordu.

// Örnek: optimizasyon hedefi olan fonksiyon  
function calculateSum(arr) {  
  let sum = 0;  
  for (let i = 0; i < arr.length; i++) {  
    sum += arr[i];  // Hot Loop - Crankshaft optimize eder  
  }  
  return sum;  
}  
  
// Full-codegen: hızlı derleme, yavaş yürütme  
// -> tüm kodu anında native code'a dönüştürür  
  
// Crankshaft: yavaş derleme, hızlı yürütme  
// -> yalnızca hot fonksiyonları seçici olarak optimize eder  

Sorunlar

  • Bellek kullanımı aşırı yüksekti (tüm fonksiyonlar native code)
  • Deoptimization sık meydana geliyordu
  • Karmaşık JavaScript pattern'lerini işlemek zordu
2. nesil: Ignition + TurboFan (2016-günümüz)

V8 ekibi 2016'da hem bellek verimliliğini hem de performansı iyileştirmek için tamamen yeni bir pipeline sundu. Ignition, JavaScript'i kompakt bytecode'a dönüştüren bir interpreter'dır ve Full-codegen'e kıyasla bellek kullanımını %50-75 azalttı. TurboFan ise Crankshaft'ın yerini alan optimize edici derleyicidir ve daha gelişmiş optimizasyonlar uygular.

// Ignition bytecode interpreter'ının çalışma biçimi  
function Component({ data }) {  
  // 1. Parsing -> AST oluşturma  
  // 2. Ignition bytecode'a dönüştürür  
  const result = data.map(item => item * 2);  
  
  // 3. Çalıştırma sayısını izleme (Feedback Vector)  
  // 4. Hot fonksiyonlar TurboFan'a aktarılır  
  return result;  
}  
  
// Gerçek bytecode örneği (basitleştirilmiş)  
/*  
  LdaNamedProperty a0, [0]    // data yükle  
  CallProperty1 [1], a0, a1   // map çağrısı  
  Return                      // sonucu döndür  
*/  

Temel iyileştirmeler:

  • Bellek verimliliği: Bytecode, native code'dan çok daha küçüktür; bu da mobil ortamlar için idealdir
  • Hızlı başlangıç: Bytecode üretimi çok hızlıdır, başlangıç yükleme süresini kısaltır
  • Kademeli optimizasyon: Yalnızca gerekli bölümler TurboFan ile optimize edilerek kaynak tasarrufu sağlanır

Inline Caching (IC) ve Hidden Classes

Inline Caching, dinamik tipli dillerin en büyük zayıflıklarından biri olan özellik erişim maliyetini dramatik biçimde azaltan bir tekniktir. JavaScript'te obj.property her çalıştırıldığında, nesnenin tipini kontrol edip özelliği bulma süreci gerekir; IC ise daha önce görülen tip bilgisini cache'leyip yeniden kullanır.

Hidden Classes (veya Maps), nesnenin yapısını tanımlayan iç metadata'dır. Aynı sırayla aynı özelliklere sahip nesneler aynı Hidden Class'ı paylaşır; bu sayede V8, C++ düzeyinde özellik erişim performansına ulaşır.

// Hidden Class geçiş örneği  
class Point {  
  constructor(x, y) {  
    this.x = x;  // Hidden Class C0 -> C1  
    this.y = y;  // Hidden Class C1 -> C2  
  }  
}  
  
// Monomorphic (tek biçimli): optimize edilebilir  
function getX(point) {  
  return point.x;  // Her zaman aynı Hidden Class  
}  
  
// Polymorphic (çok biçimli): optimize etmesi zor  
function getValue(obj) {  
  return obj.value;  // Farklı Hidden Class'lar mümkün  
}  
  
// React bileşenindeki örnek  
function UserProfile({ user }) {  
  // props yapısı tutarlıysa IC etkilidir  
  return <div>{user.name}</div>;  
}  
  
// Anti-pattern: dinamik özellik ekleme  
function BadComponent({ data }) {  
  if (someCondition) {  
    data.extraField = 'value';  // Hidden Class değişir!  
  }  
  return <div>{data.value}</div>;  
}  

Optimizasyon geri bildirim döngüsü

V8'in uyarlamalı optimizasyonu (Adaptive Optimization), çalışma sırasında toplanan çalışma zamanı bilgilerine dayanarak kodu kademeli olarak optimize eder. Bu süreç üç aşamaya ayrılır.

  1. Cold: İlk kez çalıştırılan fonksiyonlar Ignition tarafından yorumlanır
  2. Warm: Birden fazla kez çağrılırken tip geri bildirimi ve yürütme kalıpları toplanır
  3. Hot: Eşik değeri aşıldığında (genelde 1000-10000 kez), TurboFan optimizasyon yapar

Bu geri bildirim döngüsü, gerçek kullanım kalıplarına uygun optimizasyonu mümkün kılar ve gereksiz optimizasyon nedeniyle kaynak israfını önler.

// V8'in optimizasyon karar süreci  
class OptimizationExample {  
  // Cold fonksiyon: Yalnızca Ignition'da çalışır  
  rarely_called() {  
    return Math.random();  
  }  
  
  // Warm fonksiyon: tip geri bildirimi toplar  
  sometimes_called(x, y) {  
    return x + y;  // Tip bilgisi kaydedilir  
  }  
  
  // Hot fonksiyon: TurboFan ile optimize edilir  
  frequently_called(arr) {  
    // Çalıştırma sayısı > eşik => optimizasyon tetiklenir  
    let sum = 0;  
    for (let i = 0; i < arr.length; i++) {  
      sum += arr[i];  
    }  
    return sum;  
  }  
}  
  
// Tip geri bildirimi toplama örneği  
let feedback = {  
  callCount: 0,  
  parameterTypes: [],  
  returnTypes: []  
};  
  
// React tarafında: render fonksiyonları sık çağrılır, bu yüzden optimizasyon adayıdır  
function FrequentlyRendered({ items }) {  
  // TurboFan'ın optimize etme olasılığı yüksektir  
  return items.map((item, i) => (  
    <Item key={i} data={item} />  
  ));  
}  

TurboFan'ın gelişmiş optimizasyon teknikleri

TurboFan, basit bir JIT derleyici değil, son derece gelişmiş bir optimizasyon derleyicisidir. Sea of Nodes adlı bir ara gösterim (IR) kullanarak çeşitli optimizasyonlar gerçekleştirir.

// 1. Satır içine alma (Inlining)  
// Küçük fonksiyonların çağrı ek yükünü kaldırarak %10-30 performans artışı sağlar  
function add(a, b) { return a + b; }  
function calculate(x, y) {  
  return add(x, y) * 2;  
  // Optimizasyondan sonra: return (x + y) * 2;  
  // Fonksiyon çağrısı maliyeti ortadan kalkar + ek optimizasyon fırsatları oluşur  
}  
  
// 2. Kaçış analizi (Escape Analysis)  
// Geçici nesnelerin heap'e tahsisini önleyerek GC yükünü azaltır  
function createPoint() {  
  const point = { x: 10, y: 20 };  // Normalde heap'te tahsis edilir  
  return point.x + point.y;  // Nesne fonksiyonun dışına çıkmaz  
  // Optimizasyondan sonra: return 30;  // Derleme anında hesaplanır  
  // Sonuç: nesne oluşturma maliyeti 0, GC kapsamı dışında  
}  
  
// 3. Döngü optimizasyonu  
function processArray(arr) {  
  // Loop unrolling: yineleme sayısını azaltarak branch prediction hatalarını düşürür  
  for (let i = 0; i < arr.length; i += 4) {  
    // Normalde her yinelemede koşul kontrol edilir  
    // Optimizasyondan sonra: aynı anda 4 öğe işlenir  
    arr[i] = arr[i] * 2;  
    arr[i+1] = arr[i+1] * 2;  
    arr[i+2] = arr[i+2] * 2;  
    arr[i+3] = arr[i+3] * 2;  
  }  
  // Performans: 4 kata kadar artış (CPU pipeline verimliliği)  
}  
  
// 4. React'te kullanılan optimizasyon  
const MemoizedComponent = React.memo(({ data }) => {  
  // TurboFan, props karşılaştırma mantığını optimize eder  
  return <ExpensiveRender data={data} />;  
});  

Gerçek performans ölçümü ve profilleme

Derleyici optimizasyonlarının etkisi gerçek ölçümle doğrulanabilir. Chrome DevTools'un Performance sekmesi veya Node.js'in --trace-opt bayrağı kullanılarak optimizasyon süreci doğrudan gözlemlenebilir.

// Chrome DevTools'ta derleyici davranışını inceleme  
function profileFunction() {  
  // 1. İlk çalıştırma: Ignition yorumlayıcısı  
  console.time('cold');  
  calculateSum([1,2,3,4,5]);  
  console.timeEnd('cold');  
  
  // 2. Tekrarlı çalıştırma: tip geri bildirimi toplanır  
  for (let i = 0; i < 1000; i++) {  
    calculateSum([1,2,3,4,5]);  
  }  
  
  // 3. Hot çalıştırma: TurboFan tarafından optimize edilmiş kod  
  console.time('hot');  
  calculateSum([1,2,3,4,5]);  
  console.timeEnd('hot');  // Çok daha hızlı  
}  
  
// V8 bayraklarıyla optimizasyon durumunu kontrol etme  
// node --trace-opt --trace-deopt script.js  

React ve V8 derleyici optimizasyonunun sinerjisi

React, V8'in optimizasyon özellikleri dikkate alınarak tasarlanmıştır. Özellikle React 18'in Concurrent Features yapısı, V8'in optimizasyon kalıplarıyla iyi örtüşecek şekilde çalışır.

// React 18'in derleyici dostu kalıpları  
function OptimizedComponent() {  
  // 1. Tutarlı tip kullanımı  
  const [count, setCount] = useState(0);  // Her zaman number  
  
  // 2. Koşullu render optimizasyonu  
  const content = useMemo(() => {  
    // TurboFan'ın optimize etmesi kolay bir yapı  
    return count > 10 ? <Heavy /> : <Light />;  
  }, [count]);  
  
  // 3. Event handler optimizasyonu  
  const handleClick = useCallback((e) => {  
    // Aynı fonksiyon referansını korur => IC etkili olur  
    setCount(c => c + 1);  
  }, []);  
  
  return <div onClick={handleClick}>{content}</div>;  
}  
  
// React Compiler (deneysel) ile V8'in iş birliği  
// React Compiler, derleme zamanında optimizasyon yaparak  
// V8'in çalışma zamanında daha verimli çalıştırabileceği kod üretir  

Optimizasyon anti-pattern'leri ve çözümleri

V8 optimizasyonunu engelleyen yaygın anti-pattern'ler vardır. Bunlardan kaçınırsanız 2-10 kat performans artışı elde edebilirsiniz.

// Anti-pattern 1: Hidden Class kirliliği  
function bad() {  
  const obj = {};  
  obj.a = 1;      // HC1  
  obj.b = 2;      // HC2  
  delete obj.a;   // HC3 - optimizasyon iptali  
}  
  
// Çözüm: yapıyı sabitleme  
function good() {  
  const obj = { a: 1, b: 2 };  // tek seferde oluşturma  
  if (needToRemove) {  
    obj.a = undefined;  // delete yerine undefined  
  }  
}  
  
// Anti-pattern 2: aşırı polimorfizm  
function processItems(items) {  
  items.forEach(item =&gt; {  
    // item farklı tiplerde =&gt; optimizasyon zor  
    console.log(item.value);  
  });  
}  
  
// Çözüm: tipleri birleştirme  
interface Item {  
  value: number;  
  type: string;  
}  
function processTypedItems(items: Item[]) {  
  // tutarlı tip =&gt; IC etkili  
  items.forEach(item =&gt; console.log(item.value));  
}  

Derleyicilerin gelişimi, JavaScript'in çalışma hızını devrim niteliğinde iyileştirdi. Özellikle React gibi framework'ler, V8'in optimizasyon özellikleri dikkate alınarak tasarlanıyor ve geliştiricinin özel olarak uğraşmasına gerek kalmadan iyi performans verecek şekilde evriliyor. Ancak derleyici ne kadar hızlı olursa olsun, verimsiz bellek yönetimi her şeyi bozabilir. Şimdi başka bir eksendeki yeniliklere bakalım.

Tamamlayıcı stratejiler: çeşitli bellek optimizasyonu teknikleri

GC'nin temel stratejilerine ek olarak V8, çeşitli tamamlayıcı teknikler de kullanır. Bunlar belirli durumlarda GC yükünü önemli ölçüde azaltır.

1. Nesne havuzlama (Object Pooling)

Nesne havuzlama, sık oluşturulup yok edilen nesnelerin önceden yaratılıp yeniden kullanıldığı bir desendir. Bu teknik, özellikle oyunlar veya animasyonlar gibi her karede çok sayıda nesnenin üretildiği ortamlarda büyük etki gösterir.

Çalışma prensibi: Nesneleri baştan sona oluşturup yok etmek yerine, işi biten nesneyi havuza (pool) geri koyar ve ihtiyaç olduğunda yeniden kullanırsınız. Bu sayede Young Generation üzerindeki baskı azalır ve GC sıklığı belirgin biçimde düşer.

// Nesne havuzu uygulaması (simplified)  
class ObjectPool {  
  constructor(createFn, maxSize = 100) {  
    this.createFn = createFn;  
    this.pool = Array(maxSize).fill(null).map(createFn);  
  }  
  
  acquire() {  
    return this.pool.pop() || this.createFn();  
  }  
  
  release(obj) {  
    this.pool.push(obj);  
  }  
}  
  
// React'te kullanım örneği  
const bulletPool = new ObjectPool(  
  () =&gt; ({ x: 0, y: 0, active: false }),   
  1000  // 1000 mermiyi havuzlama  
);  

Performans karşılaştırması:

Gerçek ölçüm sonuçlarına göre, nesne havuzlama uygulanan parçacık sistemi, havuzlama olmayan sürüme kıyasla GC pause süresini %70 azalttı ve frame drop neredeyse tamamen ortadan kalktı. Özellikle mobil cihazlarda etki daha da büyüktü.

// Performans karşılaştırması  
const particles = [];  
for (let i = 0; i &lt; 10000; i++) {  
  // Without pooling: her seferinde yeni nesne oluşturma  
  particles.push({ x: Math.random() * 800, y: 600 });  
  
  // With pooling: nesneyi yeniden kullanma  
  // const p = pool.acquire();  
  // p.x = Math.random() * 800;  
}  
// Sonuç: GC pause %70 azaldı, frame drop çözüldü  

2. Bellek sıkıştırma (Memory Compaction)

Bellek parçalanması, uzun süre çalışan uygulamaların kronik sorunlarından biridir. V8 bunu çözmek için periyodik olarak bellek sıkıştırma işlemi yapar.

Parçalanma sorunu: Farklı boyutlardaki nesneler tekrar tekrar oluşturulup yok edildiğinde bellekte kullanılamayan küçük boşluklar oluşur. Bu da yeterli boş bellek olsa bile büyük bir nesnenin allocate edilemediği durumlara yol açar.

V8'in sıkıştırma stratejisi: Major GC sırasında yaşayan nesneleri ardışık bellek alanlarına taşıyarak boş alanları birleştirir. Bu süreç maliyetlidir, ancak idle time kullanılarak kullanıcı fark etmeden işlenir.

// Bellek parçalanması örneği  
class FragmentationExample {  
  constructor() {  
    // Parçalanmaya neden olan desen  
    this.data = [];  
    
    // Parçalanma örneği: büyük ve küçük nesnelerin karışık halde bulunması ve seçmeli kaldırılması  
    // Sonuç: boş alanlar bellekte düzensiz dağılır  
  }  
}  
  
// Geliştirici optimizasyon stratejisi  
const optimized = {  
  smallObjects: [],     // boyuta göre gruplama  
  largeObjects: [],     // parçalanmayı önleme  
  buffer: new ArrayBuffer(1024 * 1024), // ardışık bellek  
};  

3. Pointer compression

Chrome 80 ile birlikte sunulan pointer compression, V8'in bellek kullanımını çarpıcı biçimde azalttı. 64 bit sistemlerde tüm pointer'ların 8 byte yer kaplaması, JavaScript gibi yüksek seviyeli diller için aşırı bir ek yük oluşturur.

Sıkıştırma mekanizması: V8, JavaScript nesnelerini yalnızca 4GB'lık bir "cage" alanı içinde allocate eder ve bu alan içindeki adresleri 32 bit offset olarak ifade eder. Base address + 32bit offset yöntemiyle gerçek 64 bit adres yeniden oluşturulur.

Gerçek etki: Chrome ölçümlerine göre tipik web sayfalarında V8 heap bellek kullanımı ortalama %43 azaldı. React uygulamalarında ise component tree büyüdükçe etki daha da dramatik hale geldi.

// Pointer compression etkisi (Chrome 80+)  
// Before: her referans 8 bytes (64-bit)  
// After:  her referans 4 bytes (32-bit offset)  
// Sonuç: V8 heap %43 azaldı  
  
const obj = {  
  ref1: {},  // 8 bytes -&gt; 4 bytes  
  ref2: {},  // %50 bellek tasarrufu  
  ref3: {}  
};  

4. String interning

String interning, aynı içeriğe sahip string'lerin bellekte yalnızca bir kez saklandığı bir optimizasyon tekniğidir. Java'daki String Pool'a benzer bu kavram, V8 tarafından otomatik olarak uygulanır.

Otomatik interning: Kısa string'ler (genellikle 10 karakterin altı) ve sık kullanılan string'ler V8 tarafından otomatik olarak intern edilir. Örneğin "click", "hover" gibi event type string'leri binlerce kez kullanılsa da bellekte yalnızca bir kez bulunur.

Geliştirici optimizasyonu: Sabit olarak tanımlanan string'leri yeniden kullanmak, interning etkisini en üst düzeye çıkarır. Özellikle Redux action type'ları veya event adları gibi tekrar tekrar kullanılan string'leri sabit hale getirmek önemlidir.

// String interning optimizasyonu  
const EVENT_TYPES = {  
  CLICK: 'click',  
  HOVER: 'hover'  
};  
  
// V8 otomatik interning: aynı string yalnızca bir kez saklanır  
// 10.000 kez kullanılsa da bellekte 1 instance  
events.push({ type: EVENT_TYPES.CLICK });  

5. WeakMap/WeakSet ile bellek yönetimi

WeakMap ve WeakSet, ES6 ile gelen zayıf referanslı koleksiyonlardır ve bellek sızıntılarını önlemek için güçlü araçlardır.

Normal Map'in sorunu: Normal Map, anahtar olarak kullanılan nesneyi güçlü biçimde referansladığı için, o nesneye artık ihtiyaç kalmasa bile GC onu toplayamaz. Bu özellikle DOM node'larını anahtar olarak kullanırken ciddi bellek sızıntılarına yol açar.

WeakMap’in çözümü: WeakMap, anahtar nesneleri zayıf şekilde referanslar; anahtar nesneye dair başka referans kalmadığında giriş otomatik olarak kaldırılır. Bu sayede önbellekler veya meta veri depoları güvenli biçimde uygulanabilir.

Pratik kullanım: React bileşenlerinin private verilerini saklama, DOM düğümlerine bağlı verileri yönetme, geçici önbellek uygulama gibi alanlarda bellek güvenliğini garanti eder.

// WeakMap: otomatik bellek serbest bırakma  
const cache = new WeakMap();  
  
// DOM düğümü meta verisi (otomatik temizleme)  
elements.forEach(el => {  
  cache.set(el, { data: 'metadata' });  
  // el kaldırıldığında önbellek de otomatik temizlenir  
});  
  
// Map: açıkça silme gerekir (bellek sızıntısı riski)  
const map = new Map();  // güçlü referans korunur  

Bu teknikler genellikle tek başına kullanılmaktan ziyade duruma göre seçilerek uygulanır. Özellikle oyunlarda veya gerçek zamanlı uygulamalarda büyük etki gösterir.

Sonuçların ölçülmesi: Orinoco’nun gerçek etkisi

Şimdiye kadar açıklanan tüm teknolojilerin etkisini sayılarla görelim. Orinoco projesinin devreye alınmasından önce ve sonra karşılaştırıldığında etkisi net biçimde ortaya çıkıyor.

  • Orinoco öncesi (2016): GC duraklama süresi 10~50ms
  • Orinoco sonrası (2019): GC duraklama süresi 2~15ms (yaklaşık %40~60 azalma)

SPA ortamında Orinoco uygulandıktan sonra ortalama sayfa tepki süresinin yaklaşık %18 iyileştiğini gösteren sonuçlar da var.

Bu sonuçlar yeterince etkileyici olsa da, yeni bir paradigma yeniden ortaya çıktı.

WebAssembly ve V8’in optimizasyon stratejisi: çalışma zamanı mimarisi

WebAssembly (WASM), tarayıcıda native’e yakın performans sunmak için tasarlanmış düşük seviyeli bir ikili formattır. C++, Rust, Go gibi dillerle yazılan kodların tarayıcıda çalışmasını sağlar ve V8, bunu verimli biçimde yürütmek için gelişmiş optimizasyon stratejilerine sahiptir.

1. Çok katmanlı derleme stratejisi (Tiered Compilation)

Sorun: WebAssembly modülleri birkaç MB boyutuna ulaşabildiği için derleme süresi uzun olursa kullanıcı deneyimi kötüleşir. Öte yandan optimizasyon olmadan çalıştırılırsa performans avantajı kaybolur.

Çözüm: V8, JavaScript’te olduğu gibi WASM için de çok katmanlı derleme uygular. Liftoff adlı baseline derleyici hızlıca çalıştırılabilir kod üretir, TurboFan ise arka planda optimize edilmiş kodu hazırlar.

// WebAssembly çok katmanlı derleme  
async function loadWasm() {  
  const response = await fetch('module.wasm');  
  // Streaming: indirme ile eşzamanlı derleme  
  const module = await WebAssembly.compileStreaming(response);  
  
  // Liftoff: ~10ms/MB (hızlı baseline)  
  // TurboFan: ~100ms/function (arka plan optimizasyonu)  
  
  return WebAssembly.instantiate(module, imports);  
}  

2. Dynamic Tiering ve hotspot algılama

Chrome 96 ile gelen Dynamic Tiering, WASM fonksiyonlarının çalışma sıklığını dinamik olarak analiz ederek optimizasyon hedeflerini seçer. Bu özellikle mobil ortamlarda önemlidir; gereksiz optimizasyonların neden olduğu pil tüketimini önler.

Çalışma prensibi

  • İlk çalıştırma: tüm fonksiyonlar Liftoff ile derlenir
  • Hotspot algılama: yürütme sayaçlarıyla sık çağrılan fonksiyonlar belirlenir
  • Seçici optimizasyon: yalnızca eşik değeri (ör. 1000 kez) aşan fonksiyonlar TurboFan ile yeniden derlenir
  • Dinamik ayarlama: iş yüküne göre eşik değer otomatik olarak ayarlanır
// Dynamic Tiering: sıcak fonksiyonları otomatik algılama  
const funcStats = {  
  add: { calls: 0, optimized: false },  
  matrixMultiply: { calls: 0, optimized: false }  
};  
  
// Eşik değer (1000 kez) aşıldığında TurboFan optimizasyonu  
if (funcStats.matrixMultiply.calls++ > 1000) {  
  // Liftoff -> TurboFan yeniden derleme  
}  
  
// React’te WASM kullanımı  
const wasm = await WebAssembly.instantiateStreaming(  
  fetch('module.wasm')  
);  
wasm.instance.exports.processImage(data);  

3. Bellek yönetimi ve GC entegrasyonu

Mevcut sorun: WebAssembly geleneksel olarak Linear Memory adlı basit bir byte dizisi kullanıyordu. Bu, C/C++ gibi düşük seviyeli diller için uygundu ancak JavaScript nesneleriyle etkileşimde verimsizdi.

WasmGC Proposal (Chrome 119+): WebAssembly’ye çöp toplama özelliği ekleyerek JavaScript ile aynı GC’yi paylaşmasını sağlar. Bunun şu faydaları vardır.

  • JavaScript nesneleri ile WASM struct’ları arasında karşılıklı referans mümkün
  • Açık bellek yönetimine gerek yok (malloc/free olmadan otomatik GC)
  • Döngüsel referansların otomatik çözülmesi
  • Tek bir GC pause time ile öngörülebilir performans
// Bellek paylaşımı: Linear Memory  
const memory = new WebAssembly.Memory({  
  initial: 256,   // 16MB  
  maximum: 32768  // 2GB  
});  
  
// JS <-> WASM veri aktarımı  
const view = new Uint8Array(memory.buffer, ptr, size);  
view.set(data);  // JS -> WASM  
  
// WasmGC (Chrome 119+): otomatik GC  
// (type $point (struct (field $x f64) (field $y f64)))  
// JS ve WASM aynı GC’yi paylaşır  

4. SIMD ve ileri düzey optimizasyon

SIMD (Single Instruction, Multiple Data), tek bir komutla birden fazla veriyi aynı anda işleyen bir paralel işleme tekniğidir. V8, WebAssembly SIMD’yi destekleyerek CPU’nun vektör hesaplama yeteneklerini azami ölçüde kullanır.

Performans artışı örnekleri

  • Vektör toplama: 4 adet float’ı tek seferde toplama (4 kat hız)
  • Matris çarpımı: 512x512 matrislerde 30 kat daha hızlı işlem
  • Görüntü filtresi: gerçek zamanlı blur ve sharpening efektleri mümkün
  • Fizik simülasyonu: 60fps akışkan simülasyonu başarımı
// SIMD: 4 veriyi aynı anda işleme  
// JavaScript: döngüyle tek tek işleme  
for (let i = 0; i < arr.length; i++) {  
  result[i] = a[i] + b[i];  // yavaş  
}  
  
// WASM SIMD: 4'erli paralel işleme  
// (f32x4.add (v128.load a) (v128.load b))  
// 4 kat daha hızlı vektör işlemi  
  
// Performans: JS ~450ms -> WASM ~50ms -> SIMD ~15ms  

5. Kod önbellekleme ve performans optimizasyonu

Derleme maliyeti sorunu: Büyük WASM modüllerinin (>
10MB) derlenmesi birkaç saniye sürebilir. Her sayfa yüklemesinde yeniden derlenmesi kullanıcı deneyimini kötüleştirir.

V8’in önbellekleme stratejisi

  • Derlenmiş kod önbelleği: TurboFan’ın optimize ettiği makine kodunu IndexedDB’de saklama
  • Modül serileştirme: WebAssembly.Module.serialize() ile derleme sonucunu saklama
  • Hızlı yükleme: önbellek isabetinde derleme olmadan anında çalıştırma
  • Sürüm yönetimi: zaman damgası tabanlı önbellek geçersiz kılma
// WASM kod önbellekleme (IndexedDB)  
async function loadWithCache(url) {  
  // 1. Önbelleği kontrol et  
  let module = await cache.get(url);  
  
  if (!module) {  
    // 2. Derle ve kaydet  
    module = await WebAssembly.compileStreaming(  
      fetch(url)  
    );  
    await cache.store(url, module);  
  }  
  
  return module;  // Yeniden derlemeden yeniden kullan  
}  

6. Gerçek performans ölçümü

Benchmark sonuçları, WebAssembly'nin üstünlüğünü açıkça gösteriyor. Matris çarpımı gibi hesaplama yoğun işlerde JavaScript'e kıyasla 9-30 kat performans artışı sağlıyor.

Gerçek kullanım örnekleri

  • AutoCAD Web: Tarayıcıda 3D CAD render işlemini native seviyede performansla sunuyor
  • Google Earth: Büyük ölçekli 3D harita verilerini gerçek zamanlı render ediyor
  • Figma: Vektör grafik motorunu WASM ile geliştirerek hızlı tepki süresi sağlıyor
  • Photoshop Web: Görüntü filtrelerini ve efektlerini native seviyede hızla işliyor
// Performans benchmark'ı (512x512 matris çarpımı)  
// JavaScript:     ~450ms  
// WebAssembly:    ~50ms  (9x faster)  
// WASM + SIMD:    ~15ms  (30x faster)  
  
// React görüntü filtresi örneği  
const applyFilter = async (imageData) => {  
  // JS filtresi:   ~50ms  
  // WASM filtresi: ~5ms (10x faster)  
  return wasmFilters[filterType](imageData);  
};  

Bu tür WebAssembly optimizasyon teknikleri, V8'in JavaScript optimizasyonlarıyla sinerji oluşturarak tarayıcıda native seviyede performansı mümkün kılıyor. JavaScript'in iş mantığı ve UI'ı, WebAssembly'nin ise performans açısından kritik bölümleri üstlendiği hibrit yapı giderek daha yaygın hale geliyor.

Gerçek üretim ortamı optimizasyon stratejileri

Büyük ölçekli uygulamalarda bellek optimizasyonu kalıpları

1. Gmail'de Incremental DOM optimizasyonu
// Gmail'in kademeli DOM güncelleme stratejisi  
class IncrementalRenderer {  
  constructor() {  
    this.pendingUpdates = new WeakMap();  
    this.updateQueue = [];  
  }  
  
  scheduleUpdate(element, patch) {  
    // WeakMap ile GC dostu referans  
    this.pendingUpdates.set(element, patch);  
    
    // requestIdleCallback ile boşta kalma süresini kullan  
    requestIdleCallback(() => {  
      this.processBatch();  
    }, { timeout: 16 }); // 1 frame bütçesi  
  }  
  
  processBatch() {  
    const batchSize = 100;  
    for (let i = 0; i < batchSize && this.updateQueue.length; i++) {  
      const update = this.updateQueue.shift();  
      update.apply();  
    }  
  }  
}  

Sonuç: Major GC sıklığında %70 azalma, ortalama kare koruma oranında %95 başarı

2. Discord'un nesne havuzlama stratejisi
// Mesaj nesnesi havuzlama  
class MessagePool {  
  constructor(size = 1000) {  
    this.pool = [];  
    this.activeMessages = new Set();  
    
    // Önceden ayır  
    for (let i = 0; i < size; i++) {  
      this.pool.push(new Message());  
    }  
  }  
  
  acquire() {  
    let msg = this.pool.pop();  
    if (!msg) {  
      // Havuz tükendiği için dinamik olarak genişlet  
      console.warn('Pool expansion triggered');  
      msg = new Message();  
    }  
    this.activeMessages.add(msg);  
    return msg.reset();  
  }  
  
  release(msg) {  
    if (this.activeMessages.delete(msg)) {  
      this.pool.push(msg);  
    }  
  }  
}  

Sonuç: Young Generation GC'de %85 azalma, bellek kullanımında %30 azalma

Benchmark ve performans ölçüm rehberi

V8 performans ölçüm araçları
// Chrome DevTools Performance API kullanımı  
class V8Profiler {  
  static measureGC() {  
    const obs = new PerformanceObserver((list) => {  
      for (const entry of list.getEntries()) {  
        if (entry.entryType === 'measure' &&   
            entry.detail?.kind === 'gc') {  
          console.log(`GC Type: ${entry.detail.type}`);  
          console.log(`Duration: ${entry.duration}ms`);  
          console.log(`Heap Before: ${entry.detail.usedHeapSizeBefore}`);  
          console.log(`Heap After: ${entry.detail.usedHeapSizeAfter}`);  
        }  
      }  
    });  
    
    obs.observe({ entryTypes: ['measure'] });  
  }  
  
  static getHeapSnapshot() {  
    if (typeof gc !== 'undefined') {  
      gc(); // Force GC  
    }  
    
    return performance.measureUserAgentSpecificMemory();  
  }  
}  
Gerçek ölçüm verileri

Pointer Compression (Chrome 89)

Test ortamı: 8GB RAM, 4 çekirdekli CPU  
Ölçülen uygulamalar: Gmail, Google Docs, YouTube  
  
Sonuçlar:  
- V8 Heap: 1.2GB -> 684MB (%43 azalma)  
- Renderer Memory: 2.1GB -> 1.68GB (%20 azalma)  
- Major GC Time: 45ms -> 38.7ms (%14 azalma)  
- FID p95: 24ms -> 19ms  

Orinoco vs Legacy GC

Benchmark: Speedometer 2.0  
  
Legacy (2015):  
- Score: 45 ± 3  
- GC Pause p50: 23ms  
- GC Pause p99: 112ms  
- Total GC Time: 3.2s  
  
Orinoco (2019):  
- Score: 78 ± 2 (%73 iyileşme)  
- GC Pause p50: 2.1ms (%91 azalma)  
- GC Pause p99: 14ms (%87 azalma)  
- Total GC Time: 0.9s (%72 azalma)  

Üretim ortamı kontrol listesi

// V8 optimizasyon kontrol listesi  
const optimizationChecklist = {  
  // 1. Hidden Class optimizasyonu  
  avoidDynamicProperties: true,  
  useConstructorsConsistently: true,  
  
  // 2. Inline caching  
  avoidPolymorphicCalls: true,  
  limitFunctionTypes: 4,  
  
  // 3. Bellek yönetimi  
  useObjectPools: true,  
  limitClosureScopes: true,  
  preferTypedArrays: true,  
  
  // 4. GC tetiklemelerini en aza indirme  
  batchDOMUpdates: true,  
  useWeakReferences: true,  
  clearLargeObjects: true  
};  

Bu veriler, V8'in teknolojik yeniliklerinin gerçek kullanıcı deneyimi üzerindeki etkisini açıkça gösteriyor. Şimdi bu yolculuğu tamamlarken öğrendiklerimizi özetleyelim.

Bonus

Şu anda da yeni zorluklar bizi bekliyor.

  • Daha iyi WASM entegrasyonu: WasmGC'nin tam olarak uygulanması
  • Makine öğrenimi optimizasyonu: desen tabanlı otomatik ince ayar
  • Yeni donanımdan yararlanma: ARM ve RISC-V optimizasyonu

Referanslar

Henüz yorum yok.

Henüz yorum yok.