SwiftProgramlamaiOS Geliştirici

Swift, yığın kaynakları içeren değer türlerinin işlevler arasında geçirilmesi sırasında gereksiz bellek çoğalmasını nasıl önlüyor?

Hintsage yapay zeka asistanı ile mülakatları geçin

Soruya Cevap

Swift, yığın depolama alanını saran değer türleri için Copy-on-Write (COW) adında bir optimizasyon stratejisi kullanır. Atama yapıldığında derin bir kopya almak yerine, dil kopyalamayı gerçek anlamda değişiklik yapıldığında erteleyerek gerçekleştirir. Bu, değer türünün içsel olarak paylaşılan bir sınıf örneğine referans vermesi ve isKnownUniquelyReferenced çalışma zamanı fonksiyonunu kullanarak referans sayısının bir olduğu durumları tespit etmesiyle sağlanır. Değişiklik meydana geldiğinde ve referans benzersiz olduğunda, tampon yerinde değiştirilir; aksi takdirde, yazmadan önce bir kopya oluşturulur ve acele kopyalama işleminden kaynaklanan performans kaybı olmadan değer anlamı korunur.

Hayat Durumu

Ekibimiz, büyük bir CVPixelBuffer arka depolama alanını saran özel bir Image struct'ı tanımladığımız yüksek performanslı bir görüntü işleme hattı inşa ediyordu. Profil oluşturma sırasında bir sorun ortaya çıktı: Her filtre uygulaması, 4K görüntülerin üç ara kopyasını oluşturuyor, bu da her bir karede 300MB tahsisat yapılmasına neden oluyor ve iPad cihazlarında bellek uyarıları tetikliyordu.

Bu darboğazı çözmek için üç farklı yaklaşım düşündük. İlk yaklaşım, Image'ı bir struct'tan bir class'a dönüştürmekti. Bu, referans anlamlarını kullanarak kopyaları tamamen ortadan kaldırdı, ancak birden fazla işleme zincirinin aynı piksel verilerini rastgele paylaşması ve değiştirmesi nedeniyle ciddi iş parçacığı güvenliği hataları ortaya çıkardı ve bu da görsel artefaktlarla ve çözülmesi zor yarış koşullarıyla sonuçlandı.

İkinci yaklaşım, struct tanımını korumakla birlikte UnsafeMutablePointer ve memcpy optimizasyonları kullanarak manuel derin kopyalamayı uyguladı. Bu, katı değer anlamları ile güvenliği sağladı, ancak profil oluşturma, her işlev argümanının 12MB bellek tahsisi ve bit düzeyinde kopyalama işlemi tetiklediği için hedefimizden %800 daha fazla CPU süresi tükettiğini gösterdi.

Üçüncü yaklaşım, Copy-on-Write anlamlarını manuel olarak uyguladı. Gerçek CVPixelBuffer'ı tutmak için özel bir ImageBuffer class'ı oluşturduk, Image struct'ının bu sınıfa bir referans tutmasını sağladık ve tüm değiştirme yöntemlerini, değişiklikten önce isKnownUniquelyReferenced kontrolü yapacak şekilde uyguladık:

final class ImageBuffer { var pixels: CVPixelBuffer init(_ buffer: CVPixelBuffer) { self.pixels = buffer } } struct Image { private var buffer: ImageBuffer mutating func applyFilter(_ filter: Filter) { if !isKnownUniquelyReferenced(&buffer) { buffer = ImageBuffer(buffer.pixels.deepCopy()) } filter.process(buffer.pixels) } }

Referans benzersiz değilse, önce tamponu kopyaladık. Bu çözümü seçtik çünkü Swift'in değer anlamı güvenliğini korurken, salt okunur işlemler sırasında gereksiz kopyaları ortadan kaldırdı.

Sonuç, bellek baskısını %94 oranında azalttı ve kare işleme süresini görüntü başına 120ms'den 18ms'ye düşürdü, böylece uygulama, eski donanımlarda termal kısıtlamalar olmadan gerçek zamanlı video akışlarını işleyebildi.

Adayların Sıklıkla Gözden Kaçırdığı Şeyler

Neden referans sayısını manuel olarak kontrol edemiyoruz, isKnownUniquelyReferenced yerine?

Birçok aday, referans sayısını manuel olarak takip etmeyi veya bellek adreslerini karşılaştırmayı öneriyor. Ancak, isKnownUniquelyReferenced yalnızca bir sayım kontrolü değildir; görsel işlemleri yeniden sıralamasını önleyen derleyici tarafından eklenen engelleri de içerir. Bu içsel olmadan, derleyici, benzersizlik kontrolünü optimize edebilir veya çalışma zamanı, Objective-C çalışma zamanı etkileşimleri veya standart ARC sayımına görünmeyen ek sahipsiz referansları sürdüren köprüleme dönüşümleri nedeniyle yanlış pozitifler dönebilir.

COW, Swift'in özel erişim uygulamalarıyla nasıl etkileşiyor?

Adaylar genellikle COW'nın sınıfları içeren tüm değer türleri için otomatik çalıştığını varsayıyor. Ancak, Swift'in özel erişim kuralları değişikliklerin özel erişime sahip olmasını zorunlu kılar. Özel COW uygularken, isKnownUniquelyReferenced kontrolü, değişiklik başlamadan önce yapılmalı ve tampon değiştirme bu kontrol ile atomik bir şekilde gerçekleştirilmelidir. Kontrol sırasında birden çok referans tutarak bunu ihlal etmek, çalışma zamanı özel erişim ihlalleri tetikleyebilir veya benzersizlik tespitinde yanlış negatiflere neden olabilir.

COW, eşzamanlı bağlamlarda kopyalamayı önlemede ne zaman başarısız olur?

Swift 5.5'in eşzamanlılık modelinde, adaylar COW'nın iş parçacığı güvenliği sağladığını varsayıyor. Ancak, COW, yalnızca tek bir iş parçacığı içinde güvenliği sağlar. Değerleri aktör sınırları boyunca geçirirken veya onları Sendable olarak işaretlerken, derleyici, izolasyonu korumak için aceleci kopyalamayı zorlayabilir. Ayrıca, eğer arka plan sınıfı Objective-C nesneleri içeriyorsa, isKnownUniquelyReferenced, Objective-C'nin zayıf referans uygulaması nedeniyle temkinli bir şekilde yanlış dönebilir, bu da sahiplik modelini optimize etmek için yeniden yapılandırma gerektiren gereksiz kopyalar neden olabilir.