Partikül havuzlarını yönetilen dillerde kullanmaya değer mi?


10

Ben Java benim parçacık sistemi için Nesne havuzu hayata geçireceğiz, sonra buldum bu Wikipedia'da. Yeniden ifade etmek için, Java ve C # gibi yönetilen dillerde nesne havuzlarının kullanılmaya değmediği söylenir, çünkü ayırmalar C ++ gibi yönetilmeyen dillerde yüzlerce ile karşılaştırıldığında onlarca işlem gerektirir.

Ancak hepimizin bildiği gibi, her talimat oyun performansına zarar verebilir. Örneğin, bir MMO'daki istemci havuzu: istemciler havuza çok hızlı girip çıkmaz. Ancak parçacıklar saniyede onlarca kez yenilenebilir.

Soru şudur: yönetilen bir dilde parçacıklar için (özellikle ölenler ve hızlıca yeniden yaratılanlar) bir nesne havuzu kullanmaya değer mi?

Yanıtlar:


14

Evet öyle.

Tahsis süresi tek faktör değildir. Tahsisin, çöp toplama geçişini indükleme gibi performansı etkileyebileceği gibi performansı da olumsuz etkileyebilecek yan etkileri olabilir. Bunun özellikleri dilinize ve platform tercihlerinize bağlı olacaktır.

Havuzlama ayrıca havuzdaki nesneler için, örneğin hepsini bitişik dizilerde tutarak referans yerini iyileştirir. Bu, havuzun içeriğini (veya en azından canlı kısmını) yinelerken performansı artırabilir, çünkü yinelemedeki bir sonraki nesne zaten veri önbelleğinde olacaktır.

En içteki oyun döngülerinizde herhangi bir tahsisattan kaçınmaya çalışan geleneksel bilgelik, yönetilen dillerde bile geçerlidir (özellikle, XNA kullanırken 360'da). Bunun nedenleri biraz farklıdır.


+1 Ancak, yapıları kullanırken değip değmeyeceğine değinmediniz: temelde değil (havuzlama değer türleri hiçbir şey elde etmez) - bunun yerine bunları yönetmek için tek bir (veya olası bir dizi) diziniz olmalıdır.
Jonathan Dickinson

2
Java kullanarak bahsedilen OP beri yapısal şey dokunmadım ve ben değer türleri / yapıları bu dilde nasıl işlediğini aşina değilim.

Java'da hiçbir yapı yoktur, yalnızca sınıflar vardır (her zaman öbek üzerinde).
Brendan Long

1

Java için nesneleri havuzlamak * çok yararlı değildir, çünkü hala etraftaki nesneler için ilk GC döngüsü onları bellekte yeniden karıştırır, "Eden" alanından çıkarır ve süreçte potansiyel olarak kayma yerini kaybeder.

  • Herhangi bir dilde yok etmek ve benzer iş parçacıkları oluşturmak için çok pahalı olan karmaşık kaynakları bir araya getirmek her zaman yararlıdır. Bunları bir araya getirmeye değer olabilir, çünkü bunları oluşturma ve yok etme masrafının kaynağa nesne tanıtıcısı ile ilişkili bellekle ilgisi yoktur. Bununla birlikte, parçacıklar bu kategoriye uymuyor.

Nesneleri hızlıca Eden alanına ayırdığınızda Java, sıralı bir ayırıcı kullanarak hızlı seri ayırma sağlar. Bu sıralı ayırma stratejisi, mallocC'den daha hızlı, C'den daha hızlıdır, çünkü zaten düz bir sıralı olarak ayrılmış belleği bir araya getirir, ancak bireysel bellek yığınlarını serbest bırakamayacağınız dezavantajla birlikte gelir. Ayrıca, bir şeyleri süper hızlı bir şekilde tahsis etmek istiyorsanız, örneğin ondan bir şey çıkarmanız gerekmeyen bir veri yapısı için sadece her şeyi ekleyin ve daha sonra kullanın ve her şeyi daha sonra fırlatmak istiyorsanız, C'deki yararlı bir numaradır.

Tek tek nesneleri serbest bırakamamanın bu dezavantajı nedeniyle, Java GC, ilk döngüden sonra, Eden alanından ayrılan tüm belleği, belleğe izin veren daha yavaş, daha genel bir bellek ayırıcı kullanarak yeni bellek bölgelerine kopyalayacaktır. farklı bir iş parçacığında ayrı ayrı parçalar halinde serbest bırakılmalıdır. Daha sonra, kopyalanan ve bellekte başka bir yerde yaşayan tek tek nesneler ile uğraşmadan Eden mekanında ayrılan belleği bir bütün olarak atabilir. Bu ilk GC döngüsünden sonra, nesneleriniz bellekte parçalanabilir.

Nesneler bu ilk GC döngüsünden sonra parçalanabileceğinden, öncelikle bellek erişim kalıplarını (referansın yerini) iyileştirmek ve tahsis / yeniden yerleştirme yükünü azaltmak için olduğunda nesne havuzlamanın faydaları büyük ölçüde kaybolur ... tipik olarak her zaman yeni parçacıklar tahsis ederek daha iyi bir referans konum elde edersiniz ve Eden alanında hala tazeyken ve "eski" ve potansiyel olarak hafızaya dağılmadan önce bunları kullanırsınız. Bununla birlikte, son derece yardımcı olabilecek şey (Java'da performansla rakip C elde etmek gibi) parçacıklarınız için nesne kullanmaktan kaçınmak ve düz eski ilkel verileri havuzlamaktır. Basit bir örnek vermek gerekirse:

class Particle
{
    public float x;
    public float y;
    public boolean alive;
}

Gibi bir şey yapın:

class Particles
{
    // X positions of all particles. Resize on demand using
    // 'java.util.Arrays.copyOf'. We do not use an ArrayList
    // since we want to work directly with contiguously arranged
    // primitive types for optimal memory access patterns instead 
    // of objects managed by GC.
    public float x[];

    // Y positions of all particles.
    public float y[];

    // Alive/dead status of all particles.
    public bool alive[];
}

Şimdi mevcut parçacıklar için belleği yeniden kullanmak için şunları yapabilirsiniz:

class Particles
{
    // X positions of all particles.
    public float x[];

    // Y positions of all particles.
    public float y[];

    // Alive/dead status of all particles.
    public bool alive[];

    // Next free position of all particles.
    public int next_free[];

    // Index to first free particle available to reclaim
    // for insertion. A value of -1 means the list is empty.
    public int first_free;
}

Şimdi ne zaman nthparçacık ölürse, kullanılmasına izin vermiş, bu yüzden gibi ücretsiz listeye itin:

alive[n] = false;
next_free[n] = first_free;
first_free = n;

Yeni bir parçacık eklerken, ücretsiz listeden bir dizin açıp açamayacağınıza bakın:

if (first_free != -1)
{
     int index = first_free;

     // Pop the particle from the free list.
     first_free = next_free[first_free];

     // Overwrite the particle data:
     x[index] = px;
     y[index] = py;
     alive[index] = true;
     next_free[index] = -1;
}
else
{
     // If there are no particles in the free list
     // to overwrite, add new particle data to the arrays,
     // resizing them if needed.
}

Çalışmak için en hoş kod değil, ancak bununla birlikte, tüm parçacık verileri her zaman bitişik olarak saklanacağı için sıralı parçacık işleme her zaman çok önbellek dostu olduğundan çok hızlı parçacık simülasyonları elde edebilmelisiniz. Bu tür SoA temsilcisi, yansıma / dinamik dağıtım için nesne meta verileri ve doldurma konusunda endişelenmemiz gerekmediği ve sıcak alanları soğuk alanlardan ayırdığı için (örneğin, verilerle ilgilenmemiz gerekmediğinden) bellek kullanımını azaltır. fizik sırasında bir parçacığın rengi gibi alanlar geçer, bu yüzden onu kullanmamak ve tahliye etmek için bir önbellek hattına yüklemek israf olacaktır).

Kodun çalışmasını kolaylaştırmak için, yüzer dizileri, tamsayı dizileri ve boole dizilerini depolayan kendi temel yeniden boyutlandırılabilir kaplarınızı yazmaya değer olabilir. Yine jenerikleri kullanamazsınız ve ArrayListburada (en azından son kontrol ettiğimden beri), çünkü GC tarafından yönetilen nesneler gerektirir, bitişik ilkel veriler değil. Eden alanından ayrıldıktan sonra bitişik olması intgerekmeyen GC yönetimli dizilerin değil, örneğin bitişik dizisini kullanmak istiyoruz Integer.

İlkel tip dizilerle, her zaman bitişik olmaları garanti edilir ve bu nedenle son derece arzu edilen referans yerini (sıralı parçacık işleme için fark yaratan bir dünya yapar) ve nesne havuzunun sağladığı tüm faydaları elde edersiniz. Bunun yerine, bir dizi nesne ile, bunları bir anda Eden alanına ayırdığınız varsayılarak, ancak bir GC döngüsünden sonra, tüm nesneleri işaret edebileceği varsayılarak, nesneleri bitişik bir şekilde işaret etmeye başlayan bir dizi işaretçiye benzer. bellekte yer.


1
Bu konuda güzel bir yazı ve 5 yıllık java kodlamasından sonra açıkça görebiliyorum; Java GC kesinlikle aptal değil, ne de oyun programlama için yapıldı (veri yerinin ve şeylerin gerçekten umurunda olmadığından), bu yüzden istediği gibi oynamalıyız: P
Gustavo Maciel
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.