Предварительно выделить место для очереди C ++ STL - PullRequest
26 голосов
/ 20 августа 2009

Я пишу алгоритм сортировки по основанию, используя очереди, и я хотел бы, чтобы очередь STL выделяла пространство, прежде чем я начну добавлять вещи в очередь, чтобы избежать постоянных операций динамического изменения размера.

Хотя этого не существует, я хочу что-то с эффектом ...

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

таким образом, что он не будет динамически выделять память во время цикла.

Фактический код, о котором идет речь ...

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();
        }
}
}

Ответы [ 6 ]

26 голосов
/ 20 августа 2009

Скорее всего, это не проблема. В любом случае Deque выделяется в чанках, так что вы, вероятно, будете перераспределять только несколько раз. Вы определили, что это узкое место?

В любом случае, стандарт не предоставляет средства доступа к контейнеру `queue ', потому что это противоречит цели инкапсуляции.

Если вы действительно беспокоитесь, выделите пул. Это означает предварительное выделение памяти, поэтому, когда контейнер запрашивает память, он уже там. На самом деле я не могу перейти к распределителям и родственникам, это было бы излишним для SO-ответа, но поискать распределителей в Google .

По сути, вы можете указать контейнеру, откуда взять его память. Обычно это распределитель по умолчанию, который использует new и delete.

Boost предоставляет распределитель пула , и он будет выглядеть примерно так:

#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();
};

Пул выделяет память заранее, поэтому во время push() / pop().

фактическое выделение памяти не выполняется.

Я использовал list вместо deque, потому что это проще. Обычно deque превосходит list, но с распределителем, вещи, которые дали deque такое преимущество, как производительность кэша и стоимость выделения, больше не существуют. Поэтому list гораздо проще в использовании.

Вы также можете использовать кольцевой буфер , например:

#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 */
};
4 голосов
/ 20 августа 2009

Вместо этого вы можете попробовать использовать std :: deque ...

3 голосов
/ 20 августа 2009

Если вы используете одну из структур для создания своей очереди, которая поддерживает вызов функции «резерв», вы должны быть хороши. Если эта структура данных не соответствует вашим потребностям, возможно, вы захотите найти другую.

При этом вы уверены, что здесь есть проблема с производительностью?

Jacob

2 голосов
/ 20 августа 2009

Похоже, вам нужна структура данных с методом Reserve () и эффективными операциями «push» и «pop» с противоположных концов. Как насчет кольцевого буфера, обернутого вокруг std :: vector? Вы можете зарезервировать () пространство, которое вам нужно в конструкторе, а затем поддерживать индексы "front" и "end" в вашей реализации для преобразования операций "push" и "pop" в открытом интерфейсе в операции O (1) на базовом std :: вектор.

1 голос
/ 20 августа 2009

посмотрите, поможет ли это: http://www.cs.sunysb.edu/~skiena/392/programs/queue.c

1 голос
/ 20 августа 2009

Вместо очереди, как насчет использования списка вместо?

...