2009-08-20 7 views
22

J'écris un algorithme de tri radix utilisant des files d'attente et j'aimerais qu'une file d'attente STL alloue de l'espace avant de commencer à ajouter des choses à la file d'attente.Pré-allouer de l'espace pour la file STL

Même si cela n'existe pas, je veux quelque chose avec l'effet de ...

queue<int> qs(N); 
for(int i=0;i<N;++i) 
    qs.push(rand()); 

de telle sorte qu'il ne sera pas allouer dynamiquement une mémoire pendant la boucle.

Le code réel en question ...

void radix_sort() 
{ 
// Biggest number? 
int max=-1; 
for(int i=0;i<N;++i) 
    if(a[i]>max) 
     max = a[i]; 

// How many digits in it 
int maxdigits=1; 
while(max /= 10) maxdigits++; 

// Create some buckets. 
deque<int> b[10]; 
for(int i=0;i<10;++i) 
    b[i] = deque<int>(N); 

int div=1; 
// Radix Sort by digits 
for(int d=1;d<=maxdigits;++d) 
{ 
    if(d>1) 
     div*=10; 

    // Queue 
    for(int i=0;i<N;++i) 
     b[ (a[i]/div) % 10 ].push_front(a[i]); 

    // Dequeue 
    int k=0;  
    for(int q=0;q<10;++q) 
     while(b[q].size() > 0) 
     { 
      a[k++] = b[q].back(); 
      b[q].pop_back(); 
     } 
} 
} 
+1

On dirait que vous ne savez pas si cela est encore problème de performance. C'est aussi un exemple très simple, quel genre de choses envisagez-vous de faire à cette liste? Juste pousser et pop? Est-il toujours plus grand, est-il plus petit, avez-vous besoin d'un accès indexé? ... – TheJacobTaylor

+1

N'acceptez pas votre réponse si vite, certains d'entre nous ne font que répondre à votre question. Donnez-lui quelques heures. – GManNickG

Répondre

24

Les chances sont que ce n'est pas un problème. Deque Allouer en morceaux de toute façon, de sorte que vous aurez probablement réaffecter seulement quelques fois. Avez-vous déterminé que cela était un goulot d'étranglement? Quoi qu'il en soit, la norme ne donne pas d'accesseur au conteneur de la file d'attente, car cela annulerait le but de l'encapsulation.

Si vous êtes vraiment inquiet, allouer la piscine. Cela signifie préallouer la mémoire à l'avance, donc quand le conteneur demande de la mémoire, il est déjà là. Je ne peux pas vraiment passer en revue les allocateurs et les parents, ce serait exagéré pour une réponse SO, mais regardez allocators on Google. Fondamentalement, vous pouvez dire à votre conteneur d'où sa mémoire. Normalement, il s'agit de l'allocateur par défaut, qui utilise new et delete.

Boost fournit un pool allocator, et ce serait quelque chose comme ceci:

#include <list> 
#include <queue> 

// pool 
#include <boost/pool/pool_alloc.hpp> 

// helpful typedef's 
typedef boost::fast_pool_allocator<int> BoostIntAllocator; 
typedef boost::singleton_pool<boost::fast_pool_allocator_tag, sizeof(int)> BoostIntAllocatorPool; 

int main(void) 
{ 
    // specify the list as the underlying container, and inside of that, 
    // specify fast_pool_allocator as the allocator. by default, it preallocates 
    // 32 elements. 
    std::queue<int, std::list<int, BoostIntAllocator > > q; 

    /* No memory allocations take place below this comment */ 

    for (int i = 0; i < 31; ++i) 
    { 
     q.push(i); 
    } 

    /* End no allocation */ 

    // normally, the memory used by the singleton will 
    // not be free'd until after the program is complete, 
    // but we can purge the memory manually, if desired: 
    BoostIntAllocatorPool::purge_memory(); 
}; 

La piscine alloue la mémoire à l'avance, donc pas d'allocation de mémoire réelle est effectuée pendant push()/pop(). J'ai utilisé un list au lieu d'un deque parce que c'est plus simple. Normalement, un deque is superior to a list, mais avec un allocateur, les choses qui ont donné deque son avantage, comme les performances de cache et le coût d'allocation, n'existent plus.Par conséquent, un list est beaucoup plus simple à utiliser.

Vous pouvez également utiliser un circular buffer, comme par exemple:

#include <queue> 

// ring 
#include <boost/circular_buffer.hpp> 

int main(void) 
{ 
    // use a circular buffer as the container. no allocations take place, 
    // but be sure not to overflow it. this will allocate room for 32 elements. 
    std::queue<int, boost::circular_buffer<int> > q(boost::circular_buffer<int>(32)); 

    /* No memory allocations take place below this comment */ 

    for (int i = 0; i < 31; ++i) 
    { 
     q.push(i); 
    } 

    /* End no allocation */ 
}; 
+0

+1! Je ne suis pas digne !!!

+2

Définir " J'ai regardé l'implémentation de GCC et 'deque' alloue en 512 * octets * Si vous pensez que c'est mauvais, MSVC alloue en morceaux de 16 octets, les implémentations' deque' sont horribles. Je serais reconnaissant. – doug65536

2

Si vous utilisez une des structures pour composer votre file d'attente qui prend en charge la fonction « de réserve » appel, vous devriez être bon. Si cette structure de données ne répond pas à vos besoins, vous pouvez en chercher une autre. Cela étant dit, êtes-vous sûr qu'il y a un problème de performance ici?

Jacob

+0

Oui, le problème est que deque ne vous permet pas non plus de réserver explicitement de l'espace sans en éditer le contenu. Le problème de performance est que je vais pousser vers l'avant et sauter l'arrière de la file, mais je veux réserver de l'espace pour la file d'attente puisque toutes ces poussées provoqueront un redimensionnement dynamique et tueront mes performances de tri. –

3

Vous pouvez essayer d'utiliser un std :: deque à la place ...

+0

Encore un peu nouveau sur STL. Merci pour l'info plutôt évidente que je n'ai pas remarquée. =] –

+1

Notez que deque :: push_front() provoquera la croissance de la taille des conteneurs. Vous voudrez peut-être utiliser un vecteur (avec une méthode de réserve) et envelopper une pile std :: autour de ce choix de choix ... –

1

Au lieu d'une file d'attente, que diriez-vous utiliser une liste à la place?

+0

Une liste est pire qu'une deque pour une file d'attente, en général. – GManNickG

+0

errr, un deque ne répond pas à la question. une liste fait (et en fait est presque la même chose à la réponse marquée (anneau tampon, enroulé autour d'un vecteur std ::) – moogs

2

On dirait que vous avez besoin d'une structure de données avec une méthode de réserve() et efficace « push » et opérations « pop » des extrémités opposées. Que diriez-vous d'un tampon en anneau, enroulé autour d'un vecteur std ::? Vous pouvez réserver() l'espace dont vous avez besoin dans le constructeur, puis maintenir les index "front" et "end" dans votre implémentation pour traduire les opérations "push" et "pop" dans l'interface publique vers les opérations O (1) sur le std sous-jacent ::vecteur.

+0

Sonne prometteur.J'essaierai bientôt.Merci! –

+0

Je suis d'accord, j'allais suggérer un objet simple contenant des ptrs head/tail et un pool d'objets free'd Vous construisez et maintenez une liste doublement chaînée Votre méthode est plus simple – TheJacobTaylor

Questions connexes