admin管理员组

文章数量:1290991

Continuing from yesterday's question: std::priority_queue pre-allocate memory memory error

I'm working on a deterministic real time project which has an execution cycle per iteration of couple of hundred micro-seconds.

I'm facing issue where by the push operation on a std::queue and std::priority_queue randomly is taking large amount of time. Push operation generally takes around 5-25 micro-seconds but randomly takes 300 micro-seconds to 5 milli-seconds.

To overcome this issue, I decided to pre-allocate memory for my std::queue and std::priority_queue.
I'm referring to the following answer:
Development OS: Ubuntu 24.04 LTS.
My target OS: vxWorks 7
Language: C++17

Code:

#include <queue>
#include <array>
#include <vector>
#include <string>
#include <cstdint>
#include <iostream>
#include <functional>

static const std::size_t MAX_PACKET_LENGTH = 512;

struct receive_t
{
    int             data1;
    float           data2;
    std::string     data;
    receive_t()
    {
        data.reserve(MAX_PACKET_LENGTH);
    }
};


int main(int argc, char const *argv[])
{
    std::cout << "Hello, World!!!\n";
    std::vector<receive_t> container;
    container.reserve(1000);
    std::queue<receive_t, 
        std::vector<receive_t>> q 
    (
        std::move(container)
    );
    std::cout << "sizeof(receive_t) : " << sizeof(receive_t) << std::endl;
    std::cout << "sizeof(q) : " << sizeof(q) << std::endl;
    std::cout << "q.size() : " << q.size() << std::endl;
    return 0;
}

Output:

Hello, World!!!
sizeof(receive_t) : 40
sizeof(q) : 24
q.size() : 0

I've 2 questions:

  1. How do I check total memory allocated to queue? As per my calculations (sizeof(receive_t) + 512) * 1000 bytes must be allocated. MAX_PACKET_LENGTH: 512
  2. Can I attach a callback or hook to my memory allocation routine, so I can detect when my queue request additional memory.

Continuing from yesterday's question: std::priority_queue pre-allocate memory memory error

I'm working on a deterministic real time project which has an execution cycle per iteration of couple of hundred micro-seconds.

I'm facing issue where by the push operation on a std::queue and std::priority_queue randomly is taking large amount of time. Push operation generally takes around 5-25 micro-seconds but randomly takes 300 micro-seconds to 5 milli-seconds.

To overcome this issue, I decided to pre-allocate memory for my std::queue and std::priority_queue.
I'm referring to the following answer: https://stackoverflow/a/79433983/6319901
Development OS: Ubuntu 24.04 LTS.
My target OS: vxWorks 7
Language: C++17

Code:

#include <queue>
#include <array>
#include <vector>
#include <string>
#include <cstdint>
#include <iostream>
#include <functional>

static const std::size_t MAX_PACKET_LENGTH = 512;

struct receive_t
{
    int             data1;
    float           data2;
    std::string     data;
    receive_t()
    {
        data.reserve(MAX_PACKET_LENGTH);
    }
};


int main(int argc, char const *argv[])
{
    std::cout << "Hello, World!!!\n";
    std::vector<receive_t> container;
    container.reserve(1000);
    std::queue<receive_t, 
        std::vector<receive_t>> q 
    (
        std::move(container)
    );
    std::cout << "sizeof(receive_t) : " << sizeof(receive_t) << std::endl;
    std::cout << "sizeof(q) : " << sizeof(q) << std::endl;
    std::cout << "q.size() : " << q.size() << std::endl;
    return 0;
}

Output:

Hello, World!!!
sizeof(receive_t) : 40
sizeof(q) : 24
q.size() : 0

I've 2 questions:

  1. How do I check total memory allocated to queue? As per my calculations (sizeof(receive_t) + 512) * 1000 bytes must be allocated. MAX_PACKET_LENGTH: 512
  2. Can I attach a callback or hook to my memory allocation routine, so I can detect when my queue request additional memory.
Share Improve this question edited Feb 13 at 19:48 Ted Lyngmo 118k7 gold badges82 silver badges132 bronze badges asked Feb 13 at 19:15 Dark SorrowDark Sorrow 1,94718 silver badges45 bronze badges 9
  • 2 if you want deterministic performance don't use contains that can allocate, use a fixed size circular buffer. the time for push will be deterministic. – Ahmed AEK Commented Feb 13 at 19:23
  • Pushing to a priority_queue will take longer if the added element is going to be placed at the beginning of the vector than if it's place last. You could try using a deque as the inner container to see if that makes it more even. – Ted Lyngmo Commented Feb 13 at 19:26
  • "I'm referring to the following answer:" that answer is unrelated to memory allocations. Its about solving a bug related to a custom comparator – 463035818_is_not_an_ai Commented Feb 13 at 19:27
  • 1 Re: attach a callback or hook. You can implement a custom allocator (which could simply wrap the standard std::allocator plus do some logging), and pass it as std::queue<receive_t, std::vector<receive_t, MyAllocator>> q – Igor Tandetnik Commented Feb 13 at 19:41
  • If your performance is that important and you have IO (which implies threading) then you should be looking for lock-free priority queues and not use any of the dynamically allocating queues from the standard library. – Pepijn Kramer Commented Feb 13 at 19:43
 |  Show 4 more comments

1 Answer 1

Reset to default 3

You can use the tricks linked to get the underlying container then as you are using std::vector as the underlying container then you can get the capacity.

  • the queue<T,std::vector> won't allocate on push if size < capacity.
  • you can estimate the used memory by the queue using capacity * sizeof(T)
  • to get additional heap memory allocated by T, use q.size() * 512

Is there a way to access the underlying container of STL container adaptors?.

#include <queue>
#include <array>
#include <vector>
#include <string>
#include <cstdint>
#include <iostream>
#include <functional>

static const std::size_t MAX_PACKET_LENGTH = 512;

struct receive_t
{
    int             data1;
    float           data2;
    std::string     data;
    receive_t()
    {
        data.reserve(MAX_PACKET_LENGTH);
    }
};

template <class ADAPTER>
typename ADAPTER::container_type & get_container (ADAPTER &a)
{
    struct hack : ADAPTER {
        static typename ADAPTER::container_type & get (ADAPTER &a) {
            return a.*&hack::c;
        }
    };
    return hack::get(a);
}

int main(int argc, char const *argv[])
{
    std::vector<receive_t> container;
    container.reserve(1000);
    std::queue<receive_t, 
        std::vector<receive_t>> q 
    (
        std::move(container)
    );
    std::cout << "sizeof(receive_t) : " << sizeof(receive_t) << std::endl;
    std::cout << "sizeof(q) : " << sizeof(q) << std::endl;
    std::cout << "q.size() : " << q.size() << std::endl;
    std::cout << "capacity : " << get_container(q).capacity() << std::endl;
    return 0;
}
sizeof(receive_t) : 40
sizeof(q) : 24
q.size() : 0
capacity : 1000

godbolt demo


A more generic way to obtain the memory allocated is using a custom allocator for std::vector<request_t, MyAllocator> and std::basic_string<char, std::char_traits<char>, MyAllocator> and in your custom allocator you can keep track of all allocations, otherwise you can use a heap profiler to instrument the global allocator and get the memory for each object. (i know valgrind and intel VTune can do this)


If you want deterministic performance then don't use std::queue, if you use std::vector as the underlying container then pop delay depends on the number of items in the queue, and if push allocates then malloc is very non-deterministic, (worst case is a few hundred of microseconds to a millisecond, plus the time to move all elements).

Instead use a fixed size circular buffer, a good example of the interface is at Boost.Circular Buffer, but you can implement your own in around 30 lines if you don't want to use boost, both push and pop for a circular buffer only work on a single element and don't allocate any memory at runtime, you can also pool the strings used using an object pool to avoid all allocations.

one of the rules for deterministic code is that you don't allocate memory during runtime, only at startup.

本文标签: cHow to validate stdqueue preallocate memory sizeStack Overflow