Varlık sistemleri önbellek açısından nasıl etkilidir?


32

Son zamanlarda, C ++ / OpenGL oyun motorumda uygulamak için varlık sistemlerinde çok fazla okuma yapıyorum. Varlık sistemleri hakkında sürekli kandırdığım iki önemli yarar

  1. karmaşık kalıtım hiyerarşileri ile dolaştırmak zorunda olmadıkları için yeni türdeki işletmelerin kolay inşası ve
  2. önbellek verimliliği, ki anlama konusunda sorun yaşıyorum.

Teori elbette basittir; her bileşen bitişik olarak bir bellek bloğunda saklanır, bu yüzden bu bileşene önem veren sistem bellekte atlamak ve önbelleği öldürmek zorunda kalmadan tüm liste boyunca yinelenebilir. Sorun, bunun gerçekten pratik olduğu bir durumu gerçekten düşünemememdir.


İlk önce, bileşenlerin nasıl depolandıklarına ve birbirlerine nasıl referans yaptıklarına bakalım. Sistemlerin birden fazla bileşenle çalışabilmesi gerekir, yani hem oluşturma hem de fizik sisteminin dönüşüm bileşenine erişmesi gerekir. Bunu ele alan birkaç olası uygulama gördüm ve hiçbiri iyi yapamadı.

Bileşenlere işaretçileri diğer bileşenlere veya işaretçileri bileşenlere depolayan öğelere işaretçi koyabilirsiniz. Ancak, karışıma işaretçiler atar atmaz, zaten önbellek verimliliğini öldürüyorsunuz. Her bileşen dizisinin 'n' büyük olduğundan emin olabilirsiniz; burada 'n' sistemde yaşayan varlıkların sayısıdır, ancak bu yaklaşım hararetle boşa harcanır. bu, motora yeni bileşen türleri eklemeyi çok zorlaştırır, ancak yine de bir diziden diğerine atladığınız için önbellek verimliliğini ortadan kaldırır. Ayrı dizileri tutmak yerine varlık dizinizi araya sokabilirsiniz, ancak hala belleği boşa harcıyorsunuz; yeni bileşenler veya sistemler eklemeyi oldukça pahalı kılar, ancak şimdi tüm eski seviyelerinizi geçersiz kılma ve dosya kaydetme avantajına sahiptir.

Bunların hepsi varlıkların bir listede, her karede veya kene içinde doğrusal olarak işlendiğini varsaymaktadır. Gerçekte, bu genellikle durum böyle değil. Tıkanma temizleme işlemini gerçekleştirmek için bir sektör / portal oluşturucu veya bir oktree kullandığınızı varsayalım. Varlıkları bir sektör / düğümde bitişik olarak depolayabilirsiniz, ancak isterseniz ister beğenmeyeceksiniz, etrafa atlayacaksınız. O zaman başka bir düzende depolanan varlıkları tercih edebilecek başka sistemleriniz var. AI LOD ile çalışmaya başlamadan önce büyük bir listedeki varlıkları kaydetme konusunda AI tamam olabilir; daha sonra, bu listeyi oynatıcıya olan mesafeye veya diğer LOD metriklerine göre bölmek isteyeceksiniz. Fizik o oktree kullanmak isteyecek. Senaryoların umrunda değil, ne olursa olsun koşmaları gerek.

Bileşenleri "mantık" (örneğin ai, scriptler, vb.) Ve "dünya" (örn. Oluşturma, fizik, ses, vb.) Arasında bölmeyi ve her listeyi ayrı ayrı yönetmeyi görebiliyordum, ancak bu listeler birbirleriyle etkileşime girmek zorunda. AI, bir varlığın görüntülenmesi için kullanılan dönüştürme veya animasyon durumunu etkileyemiyorsa anlamsızdır.


Gerçek dünyadaki bir oyun motorunda varlık sistemleri nasıl önbellek verimli? Belki de herkesin kullandığı, ancak bahsetmediği, varlıkları küresel bir dizide depolamak ve oktree içerisinde referans vermek gibi karma bir yaklaşım var?


Lütfen günümüzde çok çekirdekli CPU'ya ve bir satırdan daha büyük önbelleğe sahip olduğunuzu unutmayın. İki sistemden erişim bilgilerine ihtiyacınız olsa bile, her ikisine de uygun olmaları muhtemeldir. Ayrıca, grafik oluşturma genellikle ayrılmıştır - tam olarak belirttiğiniz şey için (ağaçlar, sahneler, ..)
wondra

2
Varlık sistemleri her zaman önbellek etkin değildir, ancak bazı uygulamaların (benzer şeyleri başarmanın başka yolları üzerinde) bir avantajı olabilir.
Josh

Yanıtlar:


43

Varlık sistemleri hakkında sürekli olarak duyduğum iki önemli avantaj, 1) karmaşık kalıtım hiyerarşileri ile karışmak zorunda olmadıkları için yeni türdeki varlıkların kolay inşası ve 2) önbellek verimliliğidir.

(1) 'in ES / ECS değil , bileşen tabanlı tasarımın bir yararı olduğunu unutmayın . Bileşenleri "sistemler" bölümüne sahip olmayan birçok şekilde kullanabilirsiniz ve gayet iyi çalışırlar (ve hem bağımsız hem de AAA oyunlarında bu tür mimariler kullanılır).

Standart Unity nesne modeli (kullanarak GameObjectve MonoBehaviournesneler) bir ECS değil, bileşen tabanlı tasarımdır. Yeni Unity ECS özelliği elbette gerçek bir ECS'dir.

Sistemlerin birden fazla bileşenle çalışabilmesi gerekir, yani hem oluşturma hem de fizik sisteminin dönüşüm bileşenine erişmesi gerekir.

Bazı ECS, bileşen kaplarını Varlık Kimliğine göre sıralar, yani her bir gruptaki karşılık gelen bileşenlerin aynı sırada olması gerekir.

Bu, grafik bileşeni üzerinde doğrusal olarak yineleme yapıyorsanız, aynı zamanda karşılık gelen dönüşüm bileşenleri üzerinde doğrusal olarak yineleme yaptığınız anlamına gelir . Bazı dönüşümleri atlıyor olabilirsiniz (fizik yapmadığınız veya böyle yapmadığınız bir şeyi tetiklediğinizden dolayı), ancak her zaman ileriye atladığınızdan bellekte (ve özellikle çok büyük mesafeler olmadan) hala devam edersiniz verimlilik kazanımlarına sahip olmak.

Bu, Dizilerin Yapısı (SOA) 'nın HPC için önerilen yaklaşım olma biçimine benzer. CPU ve önbellek, neredeyse tek bir doğrusal diziyle başa çıkabildiği gibi birden fazla doğrusal diziyle de başa çıkabilir ve rasgele bellek erişimi ile başa çıkabileceğinden çok daha iyidir.

Bazı ECS uygulamalarında kullanılan - Birlik ECS de dahil olmak üzere - başka bir strateji, ilgili Varlıklarının Arketipine dayalı Bileşenleri tahsis etmektir. Olduğunu, Bileşenlerinin tam set ile tüm Varlıkları ( PhysicsBody, Transform) farklı Bileşenli varlıkları ayrı tahsis edilecektir (örneğin PhysicsBody, Transform, ve Renderable ).

Bu tür tasarımlardaki sistemler, önce kendi gereksinimlerine uyan (gerekli Bileşenler grubuna sahip olan) tüm Arketipleri bularak, bu Arketipler listesini yineleyerek ve eşleşen her Arketipte depolanan Bileşenleri yineleyerek çalışır. Bu, bir Arketip içinde tamamen doğrusal ve gerçek O (1) bileşen erişimine izin verir ve Sistemlerin çok düşük ek yüke sahip uyumlu Varlıklar bulmalarına izin verir (potansiyel olarak yüz binlerce Varlık aramak yerine küçük bir Arketip listesi).

Bileşenlere işaretçileri diğer bileşenlere veya işaretçileri bileşenlere depolayan öğelere işaretçi koyabilirsiniz.

Aynı varlıktaki diğer bileşenlere gönderme yapan bileşenlerin hiçbir şey depolaması gerekmez. Diğer varlıklardaki bileşenlere referans vermek için, varlık kimliğini kaydetmeniz yeterlidir.

Bir bileşenin tek bir varlık için bir defadan fazla var olmasına izin verilirse ve belirli bir örneğe başvurmanız gerekiyorsa, diğer işletmenin kimliğini ve bu varlık için bir bileşen dizini saklayın. Ancak çoğu ECS uygulaması bu duruma izin vermemektedir, çünkü özellikle bu operasyonları daha az verimli kılmaktadır.

Her bileşen dizisinin 'n' büyük olduğundan emin olabilirsiniz; burada 'n' sistemde canlı varlık sayısıdır

İşaretçileri değil, tutamaçları (örneğin, indeksler + üretim belirteçleri) kullanın ve ardından nesne referanslarını kırma korkusu olmadan dizileri yeniden boyutlandırabilirsiniz.

Herhangi bir std::dequenedenden ötürü işaretçilere izin vermek istiyorsanız ya da sorunların dizi yeniden boyutlandırma performansı.

İkincisi, bunların hepsi varlıkların her karede / kenede bir listede doğrusal olarak işlendiğini kabul eder, ancak gerçekte durum bu değildir

Varlığa bağlıdır. Evet, birçok kullanım durumu için doğru değil. Aslında, bu yüzden bileşen tabanlı tasarım (iyi) ve varlık sistemi (belirli bir MİA şekli ) arasındaki farkı şiddetle vurguluyorum .

Bileşenlerinizden bazıları kesinlikle doğrusal olarak işlenmesi kolay olacaktır. Normalde "ağaç ağır" kullanım durumlarında bile, sıkı bir şekilde paketlenmiş dizilerin kullanımından kesinlikle performans artışı olduğunu gördük (çoğunlukla, tipik bir oyunda AI ajanları gibi en fazla birkaç yüz N içeren durumlarda).

Bazı geliştiriciler ayrıca, veri yönelimli doğrusal olarak ayrılmış veri yapılarını kullanmanın performans avantajlarının, "daha akıllı" ağaç tabanlı yapıların kullanılmasının performans avantajından daha ağır olduğunu bulmuşlardır. Hepsi elbette oyuna ve özel kullanım durumlarına bağlıdır.

Tıkanma temizleme işlemini gerçekleştirmek için bir sektör / portal oluşturucu veya bir oktree kullandığınızı varsayalım. Varlıkları bir sektör / düğümde bitişik olarak depolayabilirsiniz, ancak beğenip beğenmeyeceğinize atlayarak atlayacaksınız.

Dizinin hala ne kadar yardım ettiğine şaşıracaksınız. “Her yerden” çok daha küçük bir bellek bölgesinde dolaşıyorsunuz ve hatta tüm atlamalarla birlikte önbellekte bir şeyde bulunma ihtimaliniz çok daha yüksek. Belli bir boyuta sahip bir ağaçla, her şeyi önbelleğe almayı başarabilir ve o ağaçta hiç önbellek kaçıramayabilirsiniz.

Sıkıca paketlenmiş dizilerde yaşamak için inşa edilmiş ağaç yapıları vardır. Örneğin, octree'nizde, yığın benzeri bir yapı kullanabilirsiniz (çocuklar öncesi ebeveynler, kardeşler yan yana) ve dizide daima yinelemekte olduğunuz ağacı delerken bile yardımcı olur. CPU, hafıza erişimini / önbellek aramalarını optimize eder.

Bunu yapmak için önemli bir nokta. Bir x86 CPU karmaşık bir canavardır. CPU, makine kodunuzda bir mikro-kod optimize ediciyi etkili bir şekilde çalıştırıyor, daha küçük bir mikro kod içerisine sokuyor ve talimatları yeniden sıralıyor, bellek erişim modellerini tahmin ediyor, vs. CPU veya önbellek nasıl çalışır?

O zaman başka bir düzende depolanan varlıkları tercih edebilecek başka sistemleriniz var.

Onları birden çok kez saklayabilirsiniz. Dizilerinizi çıplak minimum ayrıntılara indirdikten sonra , bu yaklaşımla gerçekte bellek tasarrufu yaptığını (64 bit işaretçilerinizi çıkardığınızdan ve daha küçük dizinler kullanabildiğinizden) bulabilirsiniz.

Ayrı dizileri saklamak yerine varlık dizinizi araya sokabilirsiniz, ancak hala belleği boşa harcıyorsunuz

Bu iyi önbellek kullanımı için antitetiktir. Tek umursadığınız dönüşümler ve grafik verileriyse, neden makine fizik ve AI ve girdi ve hata ayıklama vb. Gibi diğer tüm verileri çekerek zaman harcıyor?

Genelde ECS'ye karşı yekpare oyun objeleri lehine yapılmış olan nokta budur (gerçekte diğer bileşen tabanlı mimarilere göre uygulanabilir değil).

Ne pahasına olursa olsun, bildiğim en çok "üretim-sınıfı" ECS uygulamaları birleştirilmiş depolama kullanıyor. Daha önce bahsettiğim popüler Arketipi yaklaşımı (örneğin, Unity ECS'de kullanılır), bir Arketip ile ilişkili Bileşenler için harmanlanmış depolamayı kullanmak için açıkça ortaya çıkar.

AI, bir varlığın görüntülenmesi için kullanılan dönüştürme veya animasyon durumunu etkileyemiyorsa anlamsızdır.

AI'nın dönüşüm verisine doğrusal olarak verimli bir şekilde erişememesi, başka hiçbir sistemin bu veri düzeni optimizasyonunu etkin bir şekilde kullanamayacağı anlamına gelmez. Oyun mantık sistemlerini, geçici işlem oyun mantığı sistemlerinin genellikle işleri yapmasını engellemeden, verileri dönüştürmek için paketlenmiş bir dizi kullanabilirsiniz.

Ayrıca kod önbelleğini de unutuyorsunuz . ECS'nin sistem yaklaşımını kullandığınızda (bazı daha saf bileşen mimarisinin aksine), aynı küçük kod döngüsünü çalıştırdığınızı ve sanal fonksiyon tablolarında ileri geri Updatesıçramamış rastgele işlevler grubuna atladığınızı garanti ediyorsunuz . senin ikili. Bu nedenle AI durumunda, tüm farklı AI bileşenlerinizi (davranışlarınızı oluşturabilmeniz için kesinlikle birden fazla kişiye sahip olmanız gerekir!) Ayrı kovalarda tutmak ve en iyi kod önbellek kullanımını kullanmak için her bir listeyi ayrı ayrı işlemek istiyorsunuz.

Gecikmiş bir olay kuyruğu ile (bir sistemin bir etkinlik listesi oluşturduğu ancak sistem tüm varlıkları işlemeyi bitirene kadar gönderemediği) olayları tutarken kod önbelleğinizin iyi kullanılmasını sağlayabilirsiniz.

Her sistemin, çerçeve için hangi olayın kuyruğunda okunacağını bildiği bir yaklaşımı kullanarak, okuma olaylarını hızlı bir şekilde yapabilirsiniz. Ya da en azından olmadan daha hızlı.

Unutma, performans mutlak değildir. İyi veri yönelimli tasarımın performans avantajlarını görmeye başlamak için her bir son önbellek kaçırma işlemini ortadan kaldırmanıza gerek yoktur.

ECS mimarisi ve veri odaklı tasarım modelleriyle birçok oyun sisteminin daha iyi çalışmasını sağlamak için hala aktif araştırmalar var. Son yıllarda SIMD ile yaptığımız bazı şaşırtıcı şeylere benzer şekilde (örn. JSON ayrıştırıcıları), ECS mimarisiyle klasik oyun mimarilerine sezgisel görünmeyen ama bir dizi teklif sunan çok daha fazla şey görüyoruz. yararları (hız, çoklu iş parçacığı, test edilebilirlik, vb.).

Belki de herkesin kullandığı, fakat hiç kimsenin bahsetmediği karma bir yaklaşım var.

Geçmişte savunduğum şey bu, özellikle de ECS mimarisinden şüphelenen insanlar için: performansın kritik olduğu bileşenlere iyi veri odaklı yaklaşımlar kullanın. Sadeliğin geliştirme süresini geliştirdiği daha basit mimariyi kullanın. Her bir bileşeni, ECS'nin önerdiği gibi kesin bir aşırı tanımlamanın içine sokmayın. Bileşen mimarinizi, mantıklı oldukları yerlerde ECS benzeri yaklaşımları kolayca kullanabileceğiniz şekilde geliştirin ve ECS benzeri yaklaşımın anlam ifade etmediği (veya bir ağaç yapısından daha az anlam ifade eden) basit bileşen yapısını kullanın. .

Ben şahsen ECS'nin gerçek gücüne kendim olarak nispeten yeni bir dönüşüm yapıyorum. Her ne kadar benim için karar verme faktörü ECS hakkında nadiren dile getirilen bir şeydi: Geçmişte birlikte çalıştığım sıkı bağlı mantık yüklü bileşen tabanlı tasarımlara kıyasla oyun sistemleri ve mantık için yazma testleri yapıyor. ECS mimarileri, sadece Bileşenleri tüketen ve Bileşen güncellemelerini üreten Sistemlere tüm mantığı koyduğundan, Sistem davranışını test etmek için bir "sahte" Bileşen kümesi oluşturmak oldukça kolaydır; Oyun mantığının çoğu yalnızca Sistemlerin içinde yaşamak zorunda olduğundan, bu, tüm Sistemlerinizin test edilmesinin oyun mantığınızın oldukça yüksek kod kapsamını sağlayacağı anlamına gelir. Sistemler, sizden çok daha az karmaşıklık veya performans etkisi olan testler için sahte bağımlılıkları (örneğin GPU arayüzleri) kullanabilir.

Bir kenara, birçok insanın ne olduğunu gerçekten anlamadan ECS hakkında konuştuğunu not edebilirsiniz. Çok fazla oyun geliştiricisinin "ECS" yi "Bileşenler" ile eşitlediğini ve "Varlık Sistemi" bölümünü tamamen görmezden geldiğini gösteren, sıkıcı sıklığı olan ECS olarak adlandırılan klasik Birliği görüyorum. İnsanların büyük bir kısmı gerçek ECS'yi değil, sadece bileşen tabanlı tasarımları savunurken, ECS'ye İnternet'te çok fazla sevgi olduğunu görüyorsunuz. Bu noktada tartışmak neredeyse anlamsız; ECS, orijinal anlamından genel bir terime indirgenmiştir ve “ECS” nin “veri odaklı ECS” ile aynı anlama gelmediğini de kabul edebilirsiniz. : /


1
ECS ile ne demek istediğinizi tanımlamak (veya ona bağlantı vermek), onu genel bileşen tabanlı tasarımla karşılaştırıp / karşılaştırmak için kullanışlıdır. Ben biri için ayrımın ne olduğu konusunda net değilim. :)
Nathan Reed

Cevabınız için çok teşekkürler, hala konuyla ilgili yapacak çok araştırmam var gibi görünüyor. Beni işaret edebileceğin herhangi bir kitap var mı?
Haydn V. Harach

3
@NathanReed: ECS , varlık-sistems.wikidot.com/es-terminology gibi yerlerde belgelenmiştir . Bileşen tabanlı tasarım , yalnızca düzenli mirastan ötürü toplanmadır, ancak oyun tasarımı için yararlı olan dinamik kompozisyona odaklanır. Sistemleri veya Varlıkları kullanmayan bileşen tabanlı motorlar yazabilir (ECS terminolojisinin anlamında) ve bileşenleri bir oyun motorunda yalnızca oyun nesneleri / varlıklarından ziyade bir oyun motorunda kullanabilirsiniz, bu yüzden farkı vurguluyorum.
Sean Middleditch

2
Bu, web’deki tüm literatüre rağmen, ECS’nin okuduğum en iyi yayınlarından biri. Mega yaşasın. Öyleyse, Sean, nihayetinde, oyun geliştirme konusundaki genel yaklaşımınız nedir? Saf bir ECS? Bileşen tabanlı ve ECS arasında karma bir yaklaşım? Tasarımlarınız hakkında daha fazla bilgi edinmek isterdim! Sizi Skype'ta ele geçirmek için çok mu fazla şey istiyor yoksa bunu tartışmak için başka bir şey mi var?
Grimshaw

2
@Grimshaw: gamedev.net, sanırım reddit.com/r/gamedev'ın olduğu gibi, açık uçlu tartışmalar için iyi bir yer. Gamedev.net 'te sık sık yaşıyorum, diğer birçok parlak insan gibi. Genelde bire bir konuşmalar yapmam; Oldukça meşgulüm ve kapalı kalma süremi (yani derleme) çok az kişiye yardım etmek için harcanmayı tercih ediyorum. :)
Sean Middleditch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.