Ben bir dize arama depolamak ve karşılaştırmak için bir 32-bit dizine çevirir Basile önerdiği gibi yoğun stajyer dizeleri eğilir. Benim durumumda bu, bazen "x" adlı bir özelliğe sahip yüz binlerce ila milyonlarca bileşene sahip olduğum için yararlıdır, örneğin, genellikle komut dosyaları tarafından adlarına erişildiğinden, kullanıcı dostu bir dize adı olması gerekir.
Arama için bir trie kullanıyorum (aynı zamanda unordered_map
denendi ancak bellek havuzları tarafından desteklenen ayarlanmış trie'm en azından daha iyi performans göstermeye başladı ve aynı zamanda yapıya her erişildiğinde kilitlenmeden iş parçacığı güvenli hale getirmek daha kolaydı) ama öyle değil oluşturmak için hızlı std::string
. Mesele, benim durumumda, iki tamsayıyı eşitlik açısından kontrol etmek ve bellek kullanımını önemli ölçüde azaltmak için dizgi eşitliğini kontrol etmek gibi sonraki işlemleri hızlandırmaktır.
Sanırım bir seçenek zaten ayrılmış değerleri bir kayıt defteri türünü korumak olurdu ama kayıt defteri yedekleri daha fazla bellek ayırma daha hızlı yapmak mümkün mü?
Bir veri yapısı üzerinden tek bir aramadan çok daha hızlı bir arama yapmak zor olacak malloc
Örneğin, bir dosya gibi harici bir girişten bir bot yükü dizesi okuduğunuz bir durum varsa, benim cazibem mümkünse sıralı bir ayırıcı kullanmak olacaktır. Bu, tek bir dizenin hafızasını serbest bırakamayacağınız dezavantajla birlikte gelir. Ayırıcı tarafından toplanan tüm bellek bir kerede boşaltılmalı veya hiç boşaltılmamalıdır. Ancak sıralı bir ayırıcı, küçük bir değişken boyutlu bellek yığınlarını düz bir ardışık tarzda bir tekne yükü tahsis etmeniz gereken durumlarda, ancak daha sonra hepsini atmak için kullanışlı olabilir. Bunun sizin durumunuzda geçerli olup olmadığını bilmiyorum, ancak uygulanabilir olduğunda, sık sık ufacık bellek ayırmaları ile ilgili bir etkin noktayı düzeltmenin kolay bir yolu olabilir (önbellek özledikleri ve sayfa hataları ile ilgili olandan daha fazlası olabilir) tarafından kullanılan algoritma malloc
.
Sabit boyutlu ayırma işlemlerinin, daha sonra yeniden kullanılmak üzere belirli bellek yığınlarını serbest bırakmanızı engelleyen sıralı ayırıcı kısıtlamaları olmadan hızlandırılması daha kolaydır. Ancak, değişken boyutlu ayırmayı varsayılan ayırıcıdan daha hızlı yapmak oldukça zordur. Temel olarak malloc
, uygulanabilirliğini daraltan kısıtlamalar uygulamıyorsanız , genellikle daha hızlı olan herhangi bir bellek ayırıcı yapmak. Bir çözüm, örneğin bir tekne yükünüz varsa 8 bayt veya daha az olan tüm dizeler için sabit boyutlu bir ayırıcı kullanmaktır ve daha uzun dizeler nadir bir durumdur (yalnızca varsayılan ayırıcıyı kullanabileceğiniz). Bu, 1 baytlık dizeler için 7 baytın boşa harcandığı anlamına gelir, ancak diyelim ki zamanın% 95'i, dizeleriniz çok kısaysa, ayırmayla ilgili etkin noktaları ortadan kaldırmalıdır.
Yeni ortaya çıkan bir başka çözüm de çılgınca gelebilecek, ancak beni duyabilecek açılmamış bağlantılı listeler kullanmak.
Buradaki fikir, her açılmamış düğümü değişken boyutlu yerine sabit boyutlu yapmaktır. Bunu yaptığınızda, belleği birleştiren ve birbirine bağlı değişken boyutlu dizeler için sabit boyutlu parçalar ayıran son derece hızlı sabit boyutlu bir yığın ayırıcı kullanabilirsiniz. Bu, bellek kullanımını azaltmaz, bağlantıların maliyeti nedeniyle ekleme eğilimindedir, ancak ihtiyaçlarınıza uygun bir denge bulmak için kaydedilmemiş boyutla oynayabilirsiniz. Bu biraz tuhaf bir fikir ama artık hantal bitişik bloklarda zaten tahsis edilmiş belleği etkili bir şekilde havuzlayabildiğiniz ve hala dizeleri tek tek serbest bırakmanın avantajlarına sahip olabileceğiniz için bellekle ilgili etkin noktaları ortadan kaldırmalısınız. İşte özgürce kullanabileceğiniz basit bir ol 'sabit ayırıcı (üretimle ilgili tüylerden yoksun, başkası için yaptığım örnek):
#ifndef FIXED_ALLOCATOR_HPP
#define FIXED_ALLOCATOR_HPP
class FixedAllocator
{
public:
/// Creates a fixed allocator with the specified type and block size.
explicit FixedAllocator(int type_size, int block_size = 2048);
/// Destroys the allocator.
~FixedAllocator();
/// @return A pointer to a newly allocated chunk.
void* allocate();
/// Frees the specified chunk.
void deallocate(void* mem);
private:
struct Block;
struct FreeElement;
FreeElement* free_element;
Block* head;
int type_size;
int num_block_elements;
};
#endif
#include "FixedAllocator.hpp"
#include <cstdlib>
struct FixedAllocator::FreeElement
{
FreeElement* next_element;
};
struct FixedAllocator::Block
{
Block* next;
char* mem;
};
FixedAllocator::FixedAllocator(int type_size, int block_size): free_element(0), head(0)
{
type_size = type_size > sizeof(FreeElement) ? type_size: sizeof(FreeElement);
num_block_elements = block_size / type_size;
if (num_block_elements == 0)
num_block_elements = 1;
}
FixedAllocator::~FixedAllocator()
{
// Free each block in the list, popping a block until the stack is empty.
while (head)
{
Block* block = head;
head = head->next;
free(block->mem);
free(block);
}
free_element = 0;
}
void* FixedAllocator::allocate()
{
// Common case: just pop free element and return.
if (free_element)
{
void* mem = free_element;
free_element = free_element->next_element;
return mem;
}
// Rare case when we're out of free elements.
// Create new block.
Block* new_block = static_cast<Block*>(malloc(sizeof(Block)));
new_block->mem = malloc(type_size * num_block_elements);
new_block->next = head;
head = new_block;
// Push all but one of the new block's elements to the free stack.
char* mem = new_block->mem;
for (int j=1; j < num_block_elements; ++j)
{
void* ptr = mem + j*type_size;
FreeElement* element = static_cast<FreeElement*>(ptr);
element->next_element = free_element;
free_element = element;
}
return mem;
}
void FixedAllocator::deallocate(void* mem)
{
// Just push a free element to the stack.
FreeElement* element = static_cast<FreeElement*>(mem);
element->next_element = free_element;
free_element = element;
}