Ağ ve görüntü işleme, fizik motorları ve ışın izleme gibi performans açısından kritik alanlarda çalışan bağlantılı listeler için bulduğum en kullanışlı durumlardan biri, bağlantılı listeleri kullanmanın aslında referansın yerini iyileştirmesi ve yığın tahsislerini azaltması ve hatta bazen bellek kullanımını azaltmasıdır. basit alternatifler.
Şimdi bu, bağlantılı listelerin, genellikle tam tersini yaptıkları için tüm bunları yapabilecekleri tam bir tezat gibi görünebilir, ancak her liste düğümünün, izin vermek için yararlanabileceğimiz sabit bir boyut ve hizalama gereksinimleri olması bakımından benzersiz bir özelliği vardır. bunların bitişik olarak depolanması ve değişken boyutlu şeylerin yapamayacağı şekilde sabit zamanda kaldırılması.
Sonuç olarak, bir milyon iç içe geçmiş değişken uzunluklu alt dizi içeren değişken uzunluklu bir diziyi depolamanın analojik eşdeğerini yapmak istediğimiz bir durumu ele alalım. Somut bir örnek, bir milyon çokgeni (bazı üçgenler, bazı dörtgenler, bazı beşgenler, bazı altıgenler vb.) Depolayan dizinlenmiş bir ağdır ve bazen çokgenler ağın herhangi bir yerinden kaldırılır ve bazen çokgenler, mevcut bir çokgene bir tepe noktası eklemek için yeniden oluşturulur veya birini kaldır. Bu durumda, bir milyon minik depolarsak std::vectors
, her bir vektör için bir yığın tahsisi ve potansiyel olarak patlayıcı bellek kullanımı ile karşı karşıya kalırız. Bir milyon minik SmallVectors
bu sorunu yaygın durumlarda o kadar fazla yaşamayabilir, ancak daha sonra ayrı ayrı yığın ayrılmamış önceden tahsis edilmiş arabellekleri yine de patlayıcı bellek kullanımına neden olabilir.
Buradaki sorun, bir milyon std::vector
örneğin bir milyon değişken uzunluklu şeyi depolamaya çalışıyor olmasıdır. Değişken uzunluklu şeyler, içeriklerini öbek üzerinde başka bir yerde depolamamışlarsa, bitişik olarak çok etkili bir şekilde depolanamayacakları ve sabit zamanda (en azından çok karmaşık bir ayırıcı olmadan basit bir şekilde) kaldırılamayacakları için bir yığın tahsisi isteme eğilimindedir.
Bunun yerine şunu yaparsak:
struct FaceVertex
{
// Points to next vertex in polygon or -1
// if we're at the end of the polygon.
int next;
...
};
struct Polygon
{
// Points to first vertex in polygon.
int first_vertex;
...
};
struct Mesh
{
// Stores all the face vertices for all polygons.
std::vector<FaceVertex> fvs;
// Stores all the polygons.
std::vector<Polygon> polys;
};
... daha sonra yığın ayırma ve önbellek kaçırma sayısını önemli ölçüde azalttık. Bir yığın tahsisi gerektirmek ve eriştiğimiz her bir çokgen için potansiyel olarak zorunlu önbellek kaçırmak yerine, artık yalnızca tüm ağda depolanan iki vektörden biri kapasitelerini aştığında (amorti edilmiş bir maliyet) bu yığın tahsisine ihtiyaç duyuyoruz. Ve bir tepe noktasından diğerine geçme adımları yine de önbellek payının ıskalamasına neden olsa da, düğümler bitişik olarak depolandığından ve komşu bir tepe noktasının olabileceği bir olasılık olduğundan, her bir çokgenin ayrı bir dinamik dizi depolamasından daha azdır. çıkarılmadan önce erişilebilir olmalıdır (özellikle birçok çokgenin köşelerini aynı anda ekleyeceği ve bu da aslanın çokgen köşelerindeki payını mükemmel şekilde bitişik hale getireceği düşünülürse).
İşte başka bir örnek:
... ızgara hücrelerinin, her bir karede hareket eden 16 milyon parçacık için parçacık-parçacık çarpışmasını hızlandırmak için kullanıldığı yerlerde. Bu parçacık ızgarası örneğinde, bağlantılı listeleri kullanarak, bir parçacığı bir ızgara hücresinden diğerine yalnızca 3 endeksi değiştirerek taşıyabiliriz. Bir vektörden silmek ve diğerine geri itmek, çok daha pahalı olabilir ve daha fazla yığın tahsisatı sağlayabilir. Bağlantılı listeler ayrıca bir hücrenin belleğini 32 bit'e düşürür. Bir vektör, uygulamaya bağlı olarak, dinamik dizisini boş bir vektör için 32 bayt alabileceği noktaya önceden tahsis edebilir. Yaklaşık bir milyon ızgara hücremiz varsa, bu oldukça büyük bir farktır.
... ve bu günlerde bağlantılı listeleri en kullanışlı bulduğum yer burası ve özellikle "indekslenmiş bağlantılı liste" çeşidini faydalı buluyorum çünkü 32 bit indeksler, 64 bit makinelerdeki bağlantıların bellek gereksinimlerini yarıya indiriyor ve düğümler bir dizide bitişik olarak depolanır.
Sık sık bunları, her yerde sabit zamanlı kaldırmalara ve eklemelere izin vermek için dizine alınmış ücretsiz listelerle birleştiriyorum:
Bu durumda, next
dizin ya düğüm kaldırılmışsa bir sonraki boş dizini ya da düğüm kaldırılmamışsa bir sonraki kullanılan dizini gösterir.
Ve bu, bugünlerde bağlantılı listeler için bulduğum bir numaralı kullanım örneği. Örneğin, her biri 4 öğenin ortalamasını alan bir milyon değişken uzunlukta alt diziyi depolamak istediğimizde (ancak bazen öğeler kaldırılır ve bu alt dizilerden birine eklenir), bağlantılı liste 4 milyon her biri ayrı ayrı yığın olarak ayrılmış olan 1 milyon konteyner yerine bitişik olarak bağlantılı liste düğümleri: bir dev vektör, yani bir milyon küçük değil.