Standart Bellek Allocation Falls Yüksek Performans Kodunda Kısa Neden

Her C programır, dinamik hafıza yönetimi için [[0) ve [[Döneticileri için 0,0) üzerinde çalışır ve geniş çeşitlilikteki bir atama, nesne boyutları ve yaşamlar için tasarlanmıştır.Bir heap, ücretsiz listeler, kömürler, yanardağlar ve birçok küçük nesneyi idare eder.Bu esneklik bir maliyetle gelir: her bir tahsis ve pozisyon anahtarlama (her zaman güvenlik için), sistem aramaları ve kartpostalları gerektirir.

Çiğ hız ötesinde, parçalanma sessiz bir performans katilidir. Zamanla, ESFLT:2) Oapta küçük tahsisleri dağıtabilir, basit bir ücretsiz listeden yeniden kullanılabilir boşlukları terk edebilir. Sonuç O, hafıza kullanımını, daha yavaş gelecekteki tahsisleri ve boşanmış CPU döngüleridir.

Bu kılavuz sizi C.'de sağlam bir bellek havuzu tasarlayarak ve uygulama üzerinden yürür ve havuz inşa etmeyi, egzoz ve hizalama gibi kenar davalarını işlemek ve çok-pool senaryolarına genişletecektir.Sonunda, yakın zaman hafıza operasyonları sağlayan bir araç olacak ve yüksek performanslı boru hatlarına nasıl sığacağını öğreneceksiniz.

Core Design Principles of a Memory Pool

Bir hafıza havuzu (ayrıca bir plaka allocator veya nesne havuzu) basit bir fikir üzerinde çalışır: bir slot serbest listeden ilk yuvayı geri döndürürken, ücretsiz liste başına geri itilir.

Sabit-Size vs. Değişken-Size Havuzlar

En yaygın varyant sabit havuzdur, her slot aynı boyuttadır. Bu, havuzun hizmet ettiği nesneyi karşılaştırır - örneğin, akupFLT:3) düğümleri. Değişken-size havuzlar (ayrıca “tana allocators” olarak adlandırılır) farklı boyutlardaki tüm klübünleri de kullanabilir, ancak karmaşıklıkları tanıtırlar: farklı blok boyutları, bölme ve kömürleri yönetmeli ve hala parçalamadan kaçınmalıyız.

İşbirlikleri

Modern CPUlar, her bir slotın depolanmış bir şekilde ayarlandığında, CENGT: 4) veya SIMD vektörleri, havuz, her bir slotın depolanmış olması gerektiğini garanti etmelidir.C standart olarak, herhangi bir standart türü için uygun bir şekilde ayarlandığında - bu, en azından bir güç kullanarak veya üst üste iki katına kadar.

Thread Safety

Tek hazır uygulamalar için, senkronizasyon gerekli değildir. ancak birçok üretim sistemi bir havuza uyumlu bir şekilde giriş gerektirir: ücretsiz listeyi bir mutex ile korumak veya bir kilitsiz bağlantılı listesi atom karşılaştırma ile kullanmak - temel tek hazır sürüm sunmaktır, ancak çoklu kullanım ortamları için uzatma noktaları tartışacağız.

Sabit birSize Bellek Havuzu: Adım Tarafından Adım-

Parasal boyuttaki nesneleri depolayan bir havuz uygulayacağız. Havuz kendisi bir nokta tutan bir yapıdır: her ücretsiz slota bir sonraki ücretsiz slota bir işarettir.Bu, dış metada üst üste kadar kaçınır.

Data Structures

#include <stddef.h>
#include <stdlib.h>

// Embedded free list node
typedef struct FreeNode {
 struct FreeNode* next;
} FreeNode;

// Pool descriptor
typedef struct MemoryPool {
 size_t slot_size; // Size of each slot (after alignment rounding)
 size_t slot_count; // Number of slots in the pool
 void* pool_start; // Start of the pre‑allocated memory block
 FreeNode* free_list; // Head of the free list
} MemoryPool;

[[Din: 16|Köpekt sadece iç içe kullanılır; Boşluk aynı hafızayı işgal ettiğinde, ilk atlar bir sonraki noktalayıcı içerir.Bu nedenle, havuz kullanıcısı bu noktaya kadar veri yazar.Bu yüzden slot büyüklüğü en az aranır.

İlkleşme

İlkleşme, hafızanın büyük bir blokunu ve her yuvayı özgür listeye bağlar. Talep edilen slot boyutunu en yakın çoklu hizaya yuvarladık (bu yüzden her slotu sağlar ve bu nedenle her geri dönüş noktası doğru bir şekilde uyumludur.

#include <stdint.h> // for max_align_t

int pool_init(MemoryPool* mp, size_t object_size, size_t object_count) {
 // Round up object_size to the alignment of max_align_t
 size_t alignment = _Alignof(max_align_t);
 size_t aligned_size = (object_size + alignment - 1) & ~(alignment - 1);

 // Ensure slot is large enough to hold a FreeNode pointer
 if (aligned_size < sizeof(FreeNode))
 aligned_size = sizeof(FreeNode);

 mp->slot_size = aligned_size;
 mp->slot_count = object_count;

 // Allocate the contiguous pool memory
 size_t total_size = aligned_size * object_count;
 mp->pool_start = malloc(total_size);
 if (mp->pool_start == NULL)
 return -1; // allocation failure

 // Build the free list
 mp->free_list = (FreeNode*)mp->pool_start;
 FreeNode* current = mp->free_list;
 for (size_t i = 1; i < object_count; i++) {
 current->next = (FreeNode*)((char*)mp->pool_start + i * aligned_size);
 current = current->next;
 }
 current->next = NULL;
 return 0;
}

Biz, en katı ayar garantisi veren, [[GÖRÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞ

Allocation

Allocation ücretsiz listenin başını pops eder ve döndürürse, ücretsiz liste boşsa, havuz tükenmiştir ve karşılığında 15.000'e geri dönüyoruz.

void* pool_alloc(MemoryPool* mp) {
 if (mp->free_list == NULL) {
 return NULL; // pool exhausted
 }
 FreeNode* block = mp->free_list;
 mp->free_list = block->next;
 return (void*)block;
}

Bu O(1) ve bir avuç talimatla çalışır. hiçbir kilit, sistem aramaları yoktur.

Bir Slot Freeing

Ücretsiz listeye geri döndük. caller, işaretçinin bu havuza ait olmasını sağlamalıdır (daha sonra geçerliliği tartışacağız).

void pool_free(MemoryPool* mp, void* ptr) {
 if (ptr == NULL) return; // standard behavior like free(NULL)
 FreeNode* node = (FreeNode*)ptr;
 node->next = mp->free_list;
 mp->free_list = node;
}

Yine O(1). kömür yok, hiçbir para kazanmıyor. Ücretsiz yuva hemen yeniden kullanılabilir.

Havuz Destruction

Havuz artık gerekli olmadığında, alt tahsisi ücretsiz.

void pool_destroy(MemoryPool* mp) {
 free(mp->pool_start);
 mp->pool_start = NULL;
 mp->free_list = NULL;
 mp->slot_count = 0;
 mp->slot_size = 0;
}

Havuz yapısı hafıza sızıntılarından kaçınmak için her zaman kapsamadan önce CFONTT:20 olarak adlandırılır.

Kullanım Örnekleri

İşte 1024 tam bir örnek, bir tane tümevleri oluşturan, bir değer yazıyor, okur ve bunu ücretsiz olarak yayınlar.

#include <stdio.h>
#include <assert.h>

int main(void) {
 MemoryPool pool;
 if (pool_init(&pool, sizeof(int), 1024) != 0) {
 fprintf(stderr, "Pool initialization failed\n");
 return 1;
 }

 int* p = (int*)pool_alloc(&pool);
 if (p == NULL) {
 fprintf(stderr, "Pool exhausted\n");
 return 1;
 }

 *p = 42;
 printf("Value: %d\n", *p);

 pool_free(&pool, p);
 pool_destroy(&pool);
 return 0;
}

Gerçek bir uygulamada, her nesne türü için bir havuz tahsis edersiniz. Örneğin, bir ağ sunucusunun bir algFLT:22 ve aDANFLT:23 olması gerekir.

Gelişmiş düşünceler ve Extensions

Debugging için Allocation

Temel havuz şu anda tahsis edilen slotların takip edildiğini takip etmiyor. For debugging, biraz alan veya ayrı bir ayrılmış blok listesi ekleyebilirsiniz. Bu, çift serbest veya sızıntıları tespit etmenizi sağlar. Üretimde, takip edilen nokta genellikle kaçınılır - havuzların genelleştirilmesi, hataları hafıza zehirlenmesi yoluyla bulmak için daha kolay hale getirir.

Hafıza Zehirlenme

Bir slot özgürleştiğinde, içeriği bilinen bir desenle yazabilirsiniz (örneğin, 03.03.2012) kullanımdan sonra ücretsiz olarak kullanmayı algılamak için. Benzer şekilde, tümocating, boş olmayan bir okuma ile yuvayı doldurabilirsiniz. Zehirleme küçük bir sabit maliyet ekliyor ama boş saatler kurtarabilirsiniz.

Satış Havuz İstatistikleri

Performans ayarını için, toplam tahsis gibi karşıtları, toplam ücretsizleri ve mevcut ücretsiz sayıyı ortaya çıkarmak için basit bir yol, havuz yapısında aritme alanı, ücretsiz olarak yükseltilme ve yükseltmeye yardımcı olur.

// Add to MemoryPool: size_t free_count;
// In pool_alloc: if (mp->free_list) { mp->free_count--; ... }
// In pool_free: mp->free_count++; ...

ThreadSafe Pools

Eş zamanlı erişim için, tümoc ve ücretsiz işlevleri bir mutex ile kapatın:

#include <pthread.h>

typedef struct ThreadSafePool {
 MemoryPool pool;
 pthread_mutex_t lock;
} ThreadSafePool;

void* ts_pool_alloc(ThreadSafePool* tsp) {
 pthread_mutex_lock(&tsp->lock);
 void* ptr = pool_alloc(&tsp->pool);
 pthread_mutex_unlock(&tsp->lock);
 return ptr;
}

void ts_pool_free(ThreadSafePool* tsp, void* ptr) {
 pthread_mutex_lock(&tsp->lock);
 pool_free(&tsp->pool, ptr);
 pthread_mutex_unlock(&tsp->lock);
}

Daha düşük içerik için, akrepsiz ücretsiz bir liste düşünün. ancak ABA problemini kullanmak gerekir - birçok koncurrency derslerinde açıklanan klasik bir meydan okuma. çoğu uygulama için, per-thread havuzlar daha basit ve ölçek daha iyi.

Pool Dynamically'i büyütün

Sabit katlı havuzlar bir kez başlangıç yapamazlar.Eğer genişleyebilir bir havuza ihtiyacınız varsa, bir havuz kıkırıkını koruyabilirsiniz.Bir chunk tükendiğinde, yeni bir chunk (aynı boyutta) ve tüm yuvalarını ücretsiz listeye ekleyin.

Performans Benchmarks (Conceptual)

Modern x86-64 CPU'da tipik bir mikrobenchmark, bir havuz alloc/free döngüsü 15-30 nanosaniye alır, ancak [[Dönbellekli iş yükleri için 32-bayt nesnesi için 80-200 nanosaniye alabilir, böylece tüm nesneler daha iyi yerellik keyfini çıkarır.

Ortak Pitfalls ve Them'dan Nasıl Kaçırmak

  • [FONT=0)Mixing havuz boyutları:[Dönetici:[Dönetici:0)[0))[[değiştir | kaynağı değiştir].[değiştir | kaynağı değiştir]
  • [FONT=0)Alignment yanlış bir şekilde:[Dönetici: 0/01/14|Dönemli bir uyum gereksinimlerine sahip kalıp, (örneğin, [[DÜye) bir diziyi kapsamaz.
  • [FONT:0) {15|DÜye Olmayanlar (DÜye) : [DÜDÜye Olmayanlar) Hayırlı, yoksa, yok edici bir şekilde, bir temizlenmiş veya temiz bir şekilde temizlenmiş olur.
  • [FONT:0] havuz değişken büyüklükteki tahsisler için havuza giriş:[Dönetici:0) Farklı boyutlardaki nesnelere ihtiyacınız varsa, ayrı havuzlar oluşturun. değişken büyüklükteki havuz atıklarına sığmaya çalışır veya transkriptiğe neden olur.

Real-World Context ve daha fazla okuma

Özel hafıza havuzları yeni bir fikir değildir. Neredeyse her yüksek performanslı sistemde görünürler:

  • Linux çekirdeği, nesne önbellekleri için [slabDÜSTÜSÜSÜSTÜSÜSÜSÜSÜSÜye Olmayanlar İçin Tıklayınız).
  • Oyun motorları:0)Unreal Motor[DÜT:1) ve [[Dönetici:2) Godot[[DÜDÜ:3) aktörler ve parçacıklar için inşa edilmiş havuz tümocators sağlar.
  • Ağ kütüphaneleri (örneğin, 03.03.2012) hızlı yolda sıfır tahsis garanti etmek için bellek havuzlarını paket tamponları kullanmak.
  • ApacheFL:0)APR[[Dönemli: 1) Kütüphane Apache HTTP Server tarafından kullanılan bir havuz API içerir.

Daha derin bir çalışma için, ESRAT:0) GNU C kütüphanesinin mallok uygulamaları) neyin kaçınıldığını anlamak ve [[ŞUygun plaka tümocator belgeleri ) tasarım ilham kaynağı için.

Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç

Özel hafıza havuzu tümocators, birçok küçük, kısa ömürlü nesneleri yöneten uygulamalar için pratik, yüksek performanslı bir optimizasyondur. Saf C'deki uygulama küçük - özel iş yüklerinize 50 satırdan daha iyi hazırlanmış kod - iki parçalı, önbellekli tüm donanımlar ve genel amaçlı tüm donanımlar.Bir sonraki yüksek performanslı sistemi anlamak için bir bina bloğu kullanın, iş akışınıza, iş yüklerinize özel olarak özelleştirilebilirsiniz.