Swift, protocol soyutlaması için başlangıçta yalnızca varlık konteynerlerine (şimdi any olarak yazılmaktadır) dayanıyordu, bu da değer türlerini yığın üzerinde kutulama gerektiriyordu ve dinamik dağıtım için tanık tablolarının kullanılmasını gerektiriyordu. Swift 5.1 ile, dil, implementasyon detaylarını gizlerken derleyici için somut tür bilgilerini koruyan ters türler tanıtıldı. Bu evrim, tür silme ile ilgili performans kayıplarını—özellikle yığın tahsisi ve kaybedilen optimizasyon fırsatları—kurban etmeden soyutlamayı sağladı ve Swift 5.6'nın varlık ve belirsiz türler arasındaki açık ayırım için zemin hazırladı.
Varlık konteynerleri (any), değerleri üç kelimelik bir temsille saklar: çevrimiçi bir değer tamponu (veya büyük türler için yığın tahsisi için bir gösterici), değer tanık tablosuna bir gösterici ve protokol tanık tablosuna bir gösterici. Bu kutulama mekanizması, değer türleri için yığın tahsisini zorlar ve metod çağrıları için dinamik dağıtım gerektirir, bu da derleyicinin uzmanlık veya iç içe çağırma yapmasını engeller. Sonuç olarak, any kullanan kod, bellek baskısında artış, ARC aşırı yükü ve önbellek hataları ile karşılaşır, bu da belirsiz performansın kritik olduğu yüksek verimli veya gerçek zamanlı sistemlerde özellikle zararlıdır.
Belirsiz türler (some), somut türün derleyiciye bilindiği ama çağırandan gizlendiği ters tür kullanımı yaklaşımına dayanır, böylece kutulama gerekliliği ortadan kaldırılır ve yığın tahsisi mümkün hale gelir. Derleyici, some dönüş türlerini, tür meta verisini görünmez bir parametre olarak geçerek ve somut değerin doğal bellek düzenini dolaylılık olmadan kullanarak, genel tür parametreleriyle benzer şekilde ele alır. Bu, statik dağıtım, fonksiyon uzmanlaşması ve agresif iç içe çağırma optimizasyonlarını sağlar, ayrıca ABI kararlılığını korur; çünkü somut tür, genel arayüzün bellek düzenini değiştirmeden evrimleşebilir.
Sık frekanslı bir pazar veri işlemcisi geliştiriyorduk; burada MarketDataEvent protokolü uygulamaları borsa tarafından değişiyordu (NYSEEvent, NASDAQEvent). Sistem, saniyede milyonlarca olayı alt-10 mikro saniye gecikme ile işleme gerektiriyordu.
Problem tanımı: Başlangıç mimarisi, her ayrıştırılan olayın, varlık kutulama nedeniyle yığında tahsis yapmasına yol açan func parse() -> any MarketDataEvent kullanıyordu. Pazar dalgalanması sırasında, bu, her saniyede 50.000'den fazla tahsis oluşturuyor ve ARC koruma/salma döngülerini ve CPU önbellek sarsıntısını tetikleyerek gecikmeyi 25 mikro saniyeye yükseltiyordu, bu da hizmet düzeyi sözleşmemizi ihlal ediyordu.
Çözüm 1: any MarketDataEvent kullanmaya devam et. Artılar: Tek bir fonksiyondan farklı türlerde dönüş türlerine izin veriyor ve basit heterojen koleksiyonlar sağlıyordu. Eksiler: Tüm değer türü olayları için zorunlu yığın tahsisi, her metod çağrısı için dinamik dağıtım aşırı yükü ve kritik ayrıştırma mantığının iç içe çağrılmasını engelleyen derleyici optimizasyonları.
Çözüm 2: some MarketDataEvent (belirsiz türler) benimse. Artılar: Olayları doğrudan yığında saklayarak yığın tahsislerini ortadan kaldırdı, statik dağıtımı ve tam derleyici uzmanlaşmasını mümkün kıldı, gecikmeyi %65 azalttı. Eksiler: Fonksiyondaki tüm kod yollarının aynı somut türü döndürmesi gerekiyordu, bu da koşullu ayrıştırma mantığını ayrı işlevler veya tür spesifik ayrıştırıcılara zorunlu kılıyordu.
Çözüm 3: Genel işlev imzaları kullanın <T: MarketDataEvent> func parse() -> T. Artılar: Monomorfizasyon ile maksimum optimizasyon potansiyeli. Eksiler: Somut türleri çağıranlara tür çıkarımı ile açığa çıkardı, derleyicinin her çağrı noktasında özel kopyalar oluşturmasını sağladı ve implementasyon detaylarının kapsüllemelerini kırdı, bu da önemli ikili boyut şişkinliğine yol açtı.
Seçilen çözüm: Çözüm 2'yi uyguladık, ayrıştırıcıyı ilgili tür kısıtlamaları olan bir protokole dönüştürdük ve birincil sıcak yol için belirsiz sonuç türlerini kullandık. Nadir heterojen koleksiyon gereksinimleri için hafif bir enum sarmalayıcı tanıttık. Neden: Yığın tahsisi ve sanallaştırma ile elde edilen performans kazançları, bir tür yüzeysel dönüş türleri mimarisinin kısıtlamalarını aştı ve yeniden yapılandırma, koşullu mantığı ayrıştırıcıdan kaldırarak endişelerin ayrımını artırdı.
Sonuç: Gecikme 3.5 mikro saniyeye düştü, yığın tahsis oranı %99.7 azaldı ve CPU önbellek vuruş oranları %40 arttı, böylece sistem donanım yükseltmeleri olmadan pazar veri hacminin 4 katını işleyebildi ve istikrarlı bellek kullanımını sürdürdü.
1. Neden belirsiz sonuç türleri dayanıklı yapıların depolanan özellikleri olarak kullanılamaz ve bu sınırlama ABI kararlılığı gereksinimleriyle nasıl etkileşir?
Belirsiz türler, derleyicinin bildiği somut alt türü bildirim yerinde sabit bellek düzenini, boyutunu ve hizalamasını hesaplayabilmesi için gereklidir. Dayanıklı kütüphaneler, sürümler arasında ABI kararlılığını korumalıdır, bu da kamu yapılarındaki depolanan özelliklerin istemcilere görünür sabit kaymalar ve boyutlar gerektirdiği anlamına gelir. some türleri, somut türü genel arayüzden gizler ancak derleme zamanında bağlar; bu, alt yapıların ikili düzenini değiştirerek mevcut derlenmiş istemcileri bozacak bir değişiklik yapar. Varlıklar (any) varlık konteyneri içindeki tutarlı üç kelimelik dolaylılık katmanını kullanarak somut tür değişikliklerinden ABI'yi izole edebilirler, bu da onlara dayanıklı bağlamlarda uygulama evrimi gerektiğinde depolanan özellikler için tek uygun seçenek haline getirir.
2. Derleyici, modül sınırlarını aşarken belirsiz türler için metod dağıtımını, aynı modül içinde farklı nasıl ele alır ve ne zaman tanık tablosu dağıtımına geri döner?
Aynı modül içinde, derleyici genellikle çağrı noktasında belirsiz döndüren fonksiyonları uzmanlaştırır, somut implementasyonu iç içe çağırarak sanal dağıtımı tamamen ortadan kaldırır. Ancak, kütüphane evrimi etkin olduğunda bir modül sınırını geçtiğinde, somut tür gizlenebilir ve bu derleyici tarafından tanık tablosu dağıtımının kullanılmasına zorlayabilir. Varlıklardan farklı olarak, her zaman varlık konteynerinde saklanan tanık tablolarını kullanır, belirsiz türler tür meta verilerini gizli bir genel parametre olarak geçirir, bu da çalışma zamanının gerekli tanık tablosunu meta veri aracılığıyla bulmasını sağlar, değeri değil. Geri dönüş, derleyicinin belirsiz sınırlar için uzmanlaşamaması durumunda tanık tablosu dağıtımına dönüş yapar; ancak, yine de, dağıtım, varlık konteynerlerinin çift dolaylılık sorununu önleyerek daha iyi performans özelliklerini korur.
3. Olay türü olarak bir belirsiz türu dönüştürmek ile as? veya Mirror yansıması kullanarak bir varlık türü arasında hangi özel çalışma zamanı meta veri farkları vardır ve neden belirsiz türler bazen varlıklarla başarılı olan dönüşümleri başarısız kılabilir?
Varlık konteynerleri (any), protokol tanık tablosunu ve tür meta verisini üç kelimelik yapı içinde taşır, bu da uygunluğun anında çalışma zamanı tanımlanmasını sağlar ve varlık türüne veya onun temel somut türüne dönüşüm destekler. Belirsiz türler (some), somut türün tam meta verisini korur, ama bunu soyutlama sınırının arkasında gizler; farklı bir protokole as? ile dönüştürme, derleyicinin somut türün meta verisi aracılığıyla uygunluk tanıkları bulmak için bir çalışma zamanı araması yapmasını gerektirir. Belirsiz bir tür, somut türün açıkça uygun olmadığı protokollere dönüşüm yapmada başarısız olabilir, bu da belirsiz bildirimin farklı bir protokol vaadi söylese bile, çünkü çalışma zamanı sürecinde somut meta verilerine karşı doğrulama yapılır. Bunun tersine, varlıklar ana protokol uygunluklarını önbelleğe alır, bu da bazı dönüşümleri daha hızlı hale getirir ama somut türün tam yeteneklerini gizleyebilir, kutudan çıkarılma ve yeniden kutulanma gerektirebilir.