Kod tabanımın (ECS motoru) en merkezi kısımlarından bazılarını, daha küçük bitişik bloklar (4 megabayt yerine 4 kilobayt gibi) kullansa da, tanımladığınız veri yapısı türünde döndürüyorum.

Eklenmeye hazır (tam olmayan bloklar) için serbest bloklar için tek bir ücretsiz liste ve bu bloktaki endeksler için blok içinde bir alt serbest liste ile sabit zamanlı ekleme ve kaldırma işlemleri elde etmek için çift serbest liste kullanır. takıldıktan sonra geri kazanılmaya hazırdır.
Bu yapının artılarını ve eksilerini kapsayacağım. Bazı eksileri ile başlayalım çünkü bunlardan birkaçı var:
Eksileri
- Bu yapıya birkaç yüz milyon eleman eklemek
std::vector(tamamen bitişik bir yapıdan) yaklaşık 4 kat daha uzun sürer . Ve mikro optimizasyonlarda oldukça iyiyim ama genel durum ilk önce blok ücretsiz listesinin üstündeki ücretsiz bloğu incelemek, sonra bloğa erişmek ve bloktan ücretsiz bir dizin açmak zorunda olduğu için kavramsal olarak daha fazla iş var. boş liste, öğeyi boş konuma yazın ve sonra bloğun dolu olup olmadığını kontrol edin ve varsa bloğu serbest listeden açın. Hâlâ sabit zamanlı bir işlem ancak geri itmekten çok daha büyük bir sabit std::vector.
- İndeksleme için ekstra aritmetik ve ekstra dolaylı katman verildiğinde rastgele erişimli bir desen kullanarak öğelere erişilirken yaklaşık iki kat daha uzun sürer.
- Sıralı erişim, yineleyici her artırıldığında ek dallandırma yapmak zorunda olduğundan bir yineleyici tasarımıyla verimli bir şekilde eşleşmez.
- Bellek biraz yükü vardır, genellikle eleman başına yaklaşık 1 bittir. Öğe başına 1 bit çok fazla gelmeyebilir, ancak bunu bir milyon 16 bit tam sayı saklamak için kullanıyorsanız, bu mükemmel bir kompakt diziden% 6.25 daha fazla bellek kullanımıdır. Bununla birlikte, pratikte bu
std::vector, ayırdığı vectorfazla kapasiteyi ortadan kaldırmak için sıkıştırmadığınız sürece daha az bellek kullanma eğilimindedir . Ayrıca genellikle bu ufacık öğeleri saklamak için kullanmıyorum.
Artıları
for_eachBir blok içindeki öğelerin geri arama işleme aralıklarını alan bir işlevi kullanarak sıralı erişim, sıralı erişim hızıyla neredeyse std::vector(yalnızca% 10 fark gibi) rakip olur , bu yüzden benim için en önemli performans kullanım durumlarında çok daha az verimli değildir ( ECS motorunda geçirilen çoğu zaman sıralı erişime sahiptir).
- Tamamen boş olduklarında blokların yer değiştirmesi ile ortadan sabit zamanlı olarak çıkarılmalarına izin verir. Sonuç olarak, veri yapısının asla gerekenden çok daha fazla bellek kullanmadığından emin olmak genellikle oldukça uygundur.
- Daha sonra yerleştirme üzerine bu delikleri geri almak için bir serbest liste yaklaşımı kullanarak arkasındaki delikler bıraktığından, doğrudan kaptan çıkarılmayan elemanlara endeksleri geçersiz kılmaz.
- Bu yapı epik sayıda öğe barındırsa bile hafızanın tükenmesi konusunda çok endişelenmenize gerek yok, çünkü sadece işletim sistemi için çok sayıda bitişik kullanılmayan bulmak için zorluk çekmeyen küçük bitişik bloklar istiyor sayfaları.
- Operasyonlar genellikle bireysel bloklara lokalize olduğundan, tüm yapıyı kilitlemeden eşzamanlılık ve iplik güvenliğine iyi borç verir.
Benim için en büyük profesyonellerden biri, bu veri yapısının değişmez bir versiyonunu yapmanın önemsiz hale gelmesiydi, şöyle:

O zamandan beri, istisna güvenliği, iş parçacığı güvenliği vb. bu veri yapısı gezerek ve kazayla, ama tartışmasız kod tabanını korumayı çok daha kolay hale getirdiği için sahip olduğu en güzel faydalardan biri.
Bitişik olmayan dizilerde önbellek yeri yoktur ve bu da kötü performans sağlar. Ancak 4M blok boyutunda iyi önbellekleme için yeterli yer olacak gibi görünüyor.
Referans konumu, 4 kilobaytlık bloklar hariç, bu boyuttaki bloklarda kendinizi ilgilendiren bir şey değildir. Önbellek satırı genellikle yalnızca 64 bayttır. Önbellek hatalarını azaltmak istiyorsanız, bu blokları düzgün bir şekilde hizalamaya odaklanın ve mümkünse daha fazla sıralı erişim düzenini tercih edin.
Rasgele erişimli bellek düzenini sıralı olana dönüştürmenin çok hızlı bir yolu bir bit kümesi kullanmaktır. Diyelim ki bir tekne yükünüz var ve bunlar rastgele sırada. Sadece onları sürebilir ve bit setindeki bitleri işaretleyebilirsiniz. Daha sonra bit setinizi tekrarlayabilir ve hangi baytların sıfır olmadığını kontrol edebilirsiniz, örneğin, bir seferde 64 bit. En az bir biti ayarlanmış 64 bitlik bir kümeyle karşılaştığınızda, hangi bitlerin ayarlandığını hızlı bir şekilde belirlemek için FFS talimatlarını kullanabilirsiniz . Bitler size hangi indekslere erişmeniz gerektiğini söyler, ancak şimdi endeksleri sıralı olarak sıralarsınız.
Bunun bir yükü vardır, ancak bazı durumlarda, özellikle de bu endekslerin üzerinde defalarca döngü yapacaksanız, değerli bir değişim olabilir.
Bir öğeye erişmek o kadar basit değil, fazladan bir dolaylama seviyesi var. Bu optimize edilebilir mi? Önbellek sorunlarına neden olur mu?
Hayır, optimize edilemez. En azından rastgele erişim bu yapı ile her zaman daha pahalıya mal olacaktır. Önbellek özlemlerinizi o kadar çok artırmaz, çünkü özellikle ortak vaka yürütme yollarınız sıralı erişim kalıpları kullanıyorsa, bloklara işaretçiler dizisi ile yüksek geçici konum elde etme eğiliminde olursunuz.
4M sınırına ulaşıldıktan sonra doğrusal büyüme olduğundan, normalde olduğundan çok daha fazla ayırmaya sahip olabilirsiniz (örneğin, 1GB bellek için maksimum 250 ayırma). 4M'den sonra fazladan bellek kopyalanmaz, ancak fazladan ayırmaların büyük bellek parçalarını kopyalamaktan daha pahalı olup olmadığından emin değilim.
Pratikte kopyalama genellikle daha hızlıdır, çünkü nadir bir durumdur, sadece log(N)/log(2)toplam kez gibi bir şey meydana gelirken, aynı zamanda, bir öğeyi dolmadan ve tekrar yeniden ayrılmadan önce diziye birçok kez yazabileceğiniz kir ucuz ortak durumu aynı anda basitleştirir. Bu nedenle, tipik olarak bu tür bir yapıya daha hızlı eklemeler elde edemezsiniz çünkü ortak vaka çalışması, büyük dizilerin yeniden tahsis edilmesi gibi pahalı nadir durumla uğraşmak zorunda olmasa bile daha pahalıdır.
Tüm eksilere rağmen bu yapının birincil cazibesi, bellek kullanımını azaltmak, OOM hakkında endişelenmek zorunda kalmamak, geçersiz olmayan endeksleri ve işaretçileri saklamak, eşzamanlılık ve değişmezliktir. Kendini temizlerken ve yapıya işaretçileri ve indeksleri geçersiz kılmazken, şeyleri sabit bir zamanda ekleyip kaldırabileceğiniz bir veri yapısına sahip olmak güzeldir.