为C++ STL队列预分配空间

gt0wga4j  于 2023-02-20  发布在  其他
关注(0)|答案(7)|浏览(150)

我正在写一个使用队列的基数排序算法,我希望在我开始向队列添加东西之前,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();
        }
}
}
vngu2lb8

vngu2lb81#

很有可能这不是问题。Deque是按块分配的,所以您可能只会重新分配几次。您确定这是一个瓶颈了吗?
无论如何,标准没有给予'queue'的容器一个访问器,因为这会破坏封装的目的。
如果你真的担心,池分配。这意味着预先分配内存,所以当容器请求内存时,它已经在那里了。我真的不能仔细检查分配器和亲属,这对SO的答案来说是多余的,但是可以查找allocators on Google
基本上,你可以告诉你的容器从哪里获取内存。通常,这是默认的分配器,它使用new和delete。
Boost提供了一个pool allocator,它将如下所示:

#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使用起来简单得多。
您也可以使用circular buffer,如下所示:

#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 */
};
t0ybt7op

t0ybt7op2#

您可以尝试使用std::deque来代替...

gab6jxml

gab6jxml3#

如果你使用其中一个结构来组成你的队列,它支持“reserve”函数调用,你应该很好。如果这个数据结构不支持你的需要,你可能需要寻找另一个。
话虽如此,您确定这里存在性能问题吗?
雅各布

rqenqsqc

rqenqsqc4#

听起来你需要一个带有reserve()方法的数据结构,以及从相反的两端进行高效的“push”和“pop”操作。那么,一个环绕在std::vector周围的环形缓冲区怎么样?你可以在构造函数中保留所需的空间,然后在实现中维护“front”和“end”索引,以便将公共接口中的“push”和“pop”操作转换为底层std::vector上的O(1)操作。

nsc4cvqm

nsc4cvqm6#

不使用队列,而是使用列表怎么样?

2g32fytz

2g32fytz7#

这是一个很老的问题,但我没有看到OP问题的答案:我如何使用带有预分配缓冲区的std::queue呢?
我不知道有哪种解决方案不需要自定义分配器,但是如果你能使用自定义分配器,你就能达到你想要的效果。
概念验证示例代码可在https://github.com/vincetse/allocator上获得,我将在下面剽窃:

typedef int data_type;
typedef lazy::memory::buffer_allocator<data_type> allocator_type;
typedef std::deque<data_type, allocator_type> deque_type;
typedef std::queue<data_type, deque_type> queue_type;

const size_t buffer_size = 32 * 1024;    
data_type buffer[buffer_size];
allocator_type allocator(buffer, sizeof(buffer));
deque_type d(allocator);
queue_type q(d);
q.push(1);
q.pop();

相关问题