"Güzel" nesne yönelimli kod yazmak ile çok düşük gecikmeli kod yazmak arasında bir denge olduğunu düşünüyor musunuz? Örneğin, C ++ 'da sanal işlevlerden kaçınmak (polimorfizm yükünün üst üste gelmesi vb. Kodu yeniden yazıyor) fakat çok hızlı vb.
Gecikmeden ziyade verime daha fazla odaklanan bir alanda çalışıyorum, ancak çok kritik bir performans ve "sorta" derim .
Ancak bir problem, bu kadar çok insanın performans kavramlarını tamamen yanlış almasıdır. Acemiler genellikle hemen hemen her şeyi yanlış anlar ve tüm "hesaplama maliyeti" kavramsal modelinin yeniden çalışılması gerekir, sadece algoritmik karmaşıklık haklarını bulabilecekleri tek şeydir. Ara ürünler bir çok şeyi yanlış anlarlar. Uzmanlar bazı şeyleri yanlış anlıyor.
Önbellek özlüyor ve dal yanlış tahminleri gibi metrikler sağlayabilen doğru araçlarla ölçüm yapmak, alanındaki uzmanlık düzeyindeki tüm kişileri kontrol altında tutan şeydir.
Ölçme, neyin optimize edilmediğine de işaret ediyor . Uzmanlar genellikle acemilerden daha az zaman harcayarak daha az zaman harcıyorlar çünkü gerçek ölçülen sıcak noktaları optimize ediyorlar ve karanlıkta vahşi bıçakları optimize etmeyi denemiyorlardı. kod tabanında diğer tüm satırlar hakkında).
Performans için Tasarım
Bununla birlikte, performans için tasarım yapmanın anahtarı , arayüz tasarımında olduğu gibi tasarım bölümünden gelir . Tecrübesizlik sorunlarından biri, bazı genelleştirilmiş bağlamlarda dolaylı bir fonksiyon çağrısı gibi, mutlak uygulama metriklerinde erken bir değişim olma eğiliminde olma eğilimi göstermesidir (maliyet bir optimizasyon noktasından daha iyi anlaşılırsa) (bir dallanma bakış açısı yerine bakış açısı) tüm kod tabanı boyunca bundan kaçınmak için bir nedendir.
Maliyetler görecelidir . Dolaylı bir işlev çağrısının bir maliyeti olsa da, örneğin, tüm maliyetler görecelidir. Milyonlarca öğeden oluşan bir işlevi çağırmak için bu ücreti bir kez ödüyorsanız, bu maliyetten endişe etmek, milyarlarca dolarlık bir ürün satın almak için para biriktirmek için saat harcamak gibidir; bir kuruş çok pahalıydı.
Kaba Arayüz Tasarımı
Performansın arayüz tasarımı yönü, genellikle bu maliyetleri daha kaba bir seviyeye itmek için daha erken çabalar. Örneğin, tek bir partikül için çalışma zamanı soyutlama masrafları ödemek yerine, bu maliyeti partikül sisteminin / yayıcısının seviyesine itebilir, bir partikülü etkin bir şekilde bir uygulama detayına ve / veya bu partikül koleksiyonunun ham verisine dönüştürebiliriz.
Bu nedenle, nesne odaklı tasarımın performans tasarımıyla (gecikme veya verim olsun) uyumsuz olması gerekmez, ancak giderek ufacık granül nesneleri modellemeye odaklanan bir dilde cazibeler olabilir ve en son iyileştirici de yardım et. Tek bir noktayı temsil eden bir sınıfı, yazılımın bellek erişim düzenleri için etkili bir SoA temsili sağlayacak şekilde birleştirmek gibi şeyler yapamaz. Pürüzlülük düzeyinde modellenen arayüz tasarımıyla puan toplama, bu fırsatı sunar ve gerektiğinde daha fazla ve daha fazla optimum çözüm için yinelemeye izin verir. Böyle bir tasarım toplu bellek için tasarlanmıştır *.
* Odaklanmayı unutmayın belleğe burada değil veri veri türleri ve veri yapıları görünümünüzü değiştirmek eğiliminde olacaktır uzun süre performans açısından kritik alanlarda çalışan ve belleğe nasıl bağlanacağını görme gibi. İkili bir arama ağacı artık sabit bir tahsisatçı tarafından desteklenmediği sürece, ağaç düğümleri için muhtemelen ayrık ve önbellek dostu olmayan bellek parçaları gibi durumlarda artık sadece logaritmik karmaşıklıktan ibaret değildir. Görüntü algoritmik karmaşıklığı göz ardı etmiyor, fakat onu artık bellek düzenlerinden bağımsız olarak görmüyor. Biri ayrıca, çalışma tekrarlarını hafıza erişiminin tekrarları olarak görmeye başlar. *
Performans açısından kritik olan pek çok tasarım aslında insanların anlaması ve kullanması kolay olan üst düzey arayüz tasarımları kavramıyla çok uyumlu olabilir. Buradaki fark, bu bağlamdaki "yüksek seviye" nin, büyük miktarda veri toplanması için modellenen bir arabirim ve kaputun altında oldukça düşük seviyeli bir uygulama ile toplu bellek birikimi ile ilgili olacağıdır. Görsel bir benzetme, ses hızında giderken gerçekten rahat, kullanması ve kullanması kolay ve çok güvenli bir araba olabilir, ancak davlumbazı patlatırsanız, içeride küçük miktarda nefes alan şeytanlar vardır.
Daha kaba bir tasarımla daha verimli kilitleme kalıpları sağlamak ve kodda paralellikten faydalanmak için daha kolay bir yol ortaya çıkma eğilimindedir (multithreading burada atlayacağım kapsamlı bir konudur).
Hafıza Havuzu
Düşük gecikmeli programlamanın kritik bir yönü, muhtemelen referansın yerini ve aynı zamanda yalnızca bellek ayırma ve serbest bırakma genel hızını arttırmak için bellek üzerinde çok açık bir kontrol olacaktır. Özel bir tahsisatçı havuzlama hafızası aslında tarif ettiğimiz aynı tasarım zihniyetini yansıtıyor. Toplu için tasarlanmıştır ; kaba bir seviyede tasarlanmıştır. Hafızayı büyük bloklar halinde önceden tahsis eder ve halihazırda küçük parçalara tahsis edilen hafızayı havuzlar.
Bu fikir, pahalı şeyleri (örneğin, genel amaçlı bir tahsisatçıya karşı bir bellek öbeğini tahsis etmek gibi) daha kaba ve kaba bir seviyeye itmekle aynıdır. Bir bellek havuzu, büyük miktarda bellek ile başa çıkmak için tasarlanmıştır .
Tip Sistemleri Ayrı Bellek
Herhangi bir dilde ayrıntılı nesne yönelimli tasarımın zorluklarından biri, çoğu zaman ufacık kullanıcı tanımlı tür ve veri yapılarını tanıtmak istemesidir. Bu tipler, eğer dinamik olarak tahsis edilmişlerse, ufacık parçalara tahsis edilmek isteyebilirler.
C ++ 'da ortak bir örnek, polimorfizmin gerekli olduğu, doğal günaha bir alt sınıfın her bir örneğini genel amaçlı bir bellek ayırıcısına karşı tahsis etmek olduğu durumlar için olacaktır.
Bu, muhtemel-bitişik bellek düzenlerini, küçük bit-bit bitlerine ve adresleme aralığı boyunca dağılmış parçalara ayırır ve bu da daha fazla sayfa hatasına ve önbellek kaybına neden olur.
En düşük gecikme süresi, kekemesiz, deterministik yanıt gerektiren alanlar muhtemelen sıcak noktaların her zaman tek bir darboğaza kaymadığı, küçük verimsizliklerin gerçekten de "biriktirilebilecek" (çoğu insanın hayal ettiği bir şey) Bunları kontrol altında tutmak için bir profilleyicide yanlış bir şekilde oluyor, ancak gecikme kaynaklı alanlarda, aslında küçük verimsizliklerin biriktiği bazı nadir durumlar olabilir). Ve böyle bir birikimin en yaygın nedenlerinden birçoğu bu olabilir: ufacık bellek parçalarının her yere aşırı tahsisi.
Java gibi dillerde, bir dizi int
(ancak yine de yüksek hacimli bir arayüzün arkasında) dizisi gibi darboğaz alanlarda (dar döngülerde işlenen alanlar) mümkün olduğunda daha eski düz veri türlerinden oluşan dizilerin kullanılması yararlı olabilir. , ArrayList
kullanıcı tanımlı Integer
nesnelerden biri. Bu, tipik olarak ikincisine eşlik edecek olan bellek ayrımını önler. C ++ 'da, bellek ayırma düzenlerimiz etkin olduğunda, kullanıcı tanımlı türler bitişik olarak orada ve hatta genel bir kapsayıcı içinde tahsis edilebildiği için yapıyı bozmamız gerekmez.
Belleği Tekrar Birleştirmek
Burada bir çözüm, homojen veri türleri için ve hatta homojen veri türleri arasında bile özel bir tahsisatçıya ulaşmaktır. Küçük veri türleri ve veri yapıları bellekteki bit ve baytlara düzleştirildiğinde, homojen bir yapıya bürünür (bazı farklı hizalama gereksinimlerine rağmen). Onlara bellek merkezli bir zihniyetten bakmadığımız zaman, programlama dillerinin tip sistemi, potansiyel olarak bitişik bellek bölgelerini küçük ufacık dağınık parçalara bölmek / ayırmak ister.
Yığın, bunu önlemek ve potansiyel olarak kullanıcı tanımlı tür örneklerinin olası herhangi bir kombinasyonunu içinde saklamak için bu bellek merkezli odağı kullanır. Yığını daha fazla kullanmak, üst kısmı neredeyse her zaman bir önbellek çizgisinde oturduğu için mümkün olduğunda harika bir fikirdir, ancak aynı zamanda bir LIFO modeli olmadan bu özelliklerin bazılarını taklit eden ve ayrık veri türleri arasında belleği bitişik hale getiren bellek ayırıcıları tasarlayabiliriz. daha karmaşık bellek ayırma ve ayırma kalıpları için bile tıkanmalar.
Modern donanım, bitişik bellek bloklarını işlerken (aynı sayfa önbelleğine, aynı sayfaya, örneğin tekrar tekrar erişerek) erişirken en üst noktada olacak şekilde tasarlanmıştır. Buradaki anahtar kelime bitişiktir, çünkü yalnızca ilgi çekici veriler varsa yararlıdır. Bu yüzden performansın anahtarının (aynı zamanda zorluğunun) çoğu, ayrıştırılmış bellek parçalarını tekrar tahliye etmeden önce bütünlüklerine erişen bitişik bloklar halinde tekrar biraraya getirmektir. Programlama dillerinde özellikle kullanıcı tanımlı türlerin zengin tip sistemi buradaki en büyük engel olabilir, ancak uygun olduğunda özel bir tahsisatçı ve / veya hantal tasarımlarıyla soruna her zaman ulaşabilir ve çözebiliriz.
Çirkin
"Çirkin" demek zor. Bu öznel bir ölçümdür ve performans açısından kritik bir alanda çalışan bir kişi "güzellik" fikrini çok daha fazla veri odaklı ve toplu işleyen arayüzlere odaklanan bir fikirle değiştirmeye başlayacaktır.
Tehlikeli
"Tehlikeli" daha kolay olabilir. Genel olarak, performans daha düşük seviyeli kodlara ulaşmak ister. Örneğin, bir bellek ayırıcısı uygulamak, veri türlerinin altına ulaşmadan ve tehlikeli düzeyde ham bit ve bayt düzeyinde çalışmadan mümkün değildir. Sonuç olarak, performans açısından kritik olan bu alt sistemlerdeki dikkatli test prosedürüne odaklanmayı artırarak, uygulanan optimizasyon düzeyiyle testin bütünlüğünü ölçeklendirmeye yardımcı olabilir.
Güzellik
Bununla birlikte, bunların tamamı uygulama detay düzeyinde olacaktır. Hem kıdemli hem de performans açısından kritik bir zihniyet anlayışında “güzellik” uygulama detaylarından ziyade arayüz tasarımlarına yönelme eğilimindedir. Arayüz tasarımı değişikliği karşısında ortaya çıkabilecek birleşme ve basamaklı kırılmalar nedeniyle uygulamalardan ziyade "güzel", kullanışlı, güvenli, verimli arayüzler aramak üstel olarak daha yüksek bir öncelik haline geliyor. Uygulamalar herhangi bir zamanda değiştirilebilir. Genellikle, gerektiğinde performansa ve ölçümlerde belirtildiği gibi tekrarlamaya devam ediyoruz. Arayüz tasarımının anahtarı, tüm sistemleri bozmadan bu tür yinelemelere yer açmak için yeterince kaba bir model oluşturmaktır.
Aslında, emektarların performans açısından kritik gelişime odaklanmasının, genellikle bir dizi performansa sahip büyük ölçekli bir kod temeli içerdiğinden, genellikle genel olarak SE öğrencisi olan güvenlik, test etme, bakım yapılabilirliğe ağırlıklı bir odaklanma eğilimi göstereceğini düşünüyorum. Kritik alt sistemler (parçacık sistemleri, görüntü işleme algoritmaları, video işleme, ses geri bildirimi, ışın izleyicileri, ağ motorları, vb.) bir bakım kabusunda boğulmayı önlemek için yazılım mühendisliğine çok dikkat etmelidir. Dışarıdaki en şaşırtıcı derecede etkili ürünlerin çoğu zaman da en az sayıda böceğe sahip olması bir tesadüf değildir.
TL; DR
Her neyse, bu benim konuyla ilgili, performans açısından kritik olan alanlardaki önceliklerden, gecikmeyi azaltabilecek ve küçük verimsizliklerin birikmesine neden olan ve gerçekte "güzelliği" neyin oluşturduğu (şeylere en verimli şekilde bakarken) olan şey.