Почему объект, размещенный в разделяемой оперативной памяти boost, занимает больше памяти, чем требуется?

Для приведенной ниже программы, использующей межпроцессорную общую память Boost,

#include <iostream>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/list.hpp>
#include <iostream>

#define SHARED_MEMORY_NAME "SO12439099-MySharedMemory"
#define DATAOUTPUT "OutputFromObject"
#define INITIAL_MEM 650000
#define STATE_MATRIX_SIZE 4

using namespace std;
namespace bip = boost::interprocess;


class SharedObject
{
public:
    unsigned int tNumber;
    bool pRcvdFlag;
    bool sRcvdFlag;
    unsigned long lTimeStamp; 
};

typedef bip::allocator<SharedObject, bip::managed_shared_memory::segment_manager> ShmemAllocator;
typedef bip::list<SharedObject, ShmemAllocator> SharedMemData; 


int main()
{
        bip::managed_shared_memory* seg;
        SharedMemData *sharedMemOutputList;

        bip::shared_memory_object::remove(DATAOUTPUT);
        seg = new bip::managed_shared_memory(bip::create_only, DATAOUTPUT, INITIAL_MEM);
        const ShmemAllocator alloc_inst(seg->get_segment_manager());
        sharedMemOutputList = seg->construct<SharedMemData>("TrackOutput")(alloc_inst);

        std::size_t beforeAllocation = seg->get_free_memory();
        std::cout<<"\nBefore allocation = "<< beforeAllocation <<"\n";
        SharedObject temp;
        sharedMemOutputList->push_back(temp);
        std::size_t afterAllocation = seg->get_free_memory();
        std::cout<<"After allocation = "<< afterAllocation <<"\n";        
        std::cout<<"Difference = "<< beforeAllocation - afterAllocation <<"\n";        
        std::cout<<"Size of SharedObject = "<< sizeof(SharedObject) <<"\n";   
        std::cout<<"Size of SharedObject's temp instance = "<< sizeof(temp) <<"\n";           
        seg->destroy<SharedMemData>("TrackOutput");
        delete seg;            
}//main

Выход:

Before allocation = 649680
After allocation = 649632
Difference = 48
Size of SharedObject = 16
Size of SharedObject's temp instance = 16

Если размер SharedObject и это экземпляр 16 байтов, тогда как может быть разница в распределении 48? Даже если заполнение было выполнено автоматически, его все равно слишком много, чтобы учесть его в 3 раза (для более крупных структур - в 1,33 раза).
Из-за этого я не могу выделить и динамически увеличить общую память надежно. Если SharedObject содержит список, который динамически растет, что может добавить к неопределенности распределения пространства еще больше.

Как можно безопасно справиться с этими ситуациями?

PS: чтобы запустить программу, вы должны связать pthread библиотека, а также librt.so,

Обновить:

Это шаблон использования памяти, который я получил, когда я табулировал значения для нескольких прогонов (memory increase столбец в основном текущая строка memory used столбец минус предыдущая строка memory used column):

╔═════════════╦════════════════╦═════════════════╗
Used используемая память size размер структуры increase увеличение памяти ║
╠═════════════╬════════════════╬═════════════════╣
║ 48 ║ 1 ║ ║
║ 48 ║ 4 ║ 0 ║
║ 48 ║ 8 ║ 0 ║
║ 48 ║ 16 ║ 0 ║
║ 64 ║ 32 ║ 16 ║
║ 64 ║ 40 ║ 0 ║
║ 80 ║ 48 ║ 16 ║
║ 96 ║ 64 ║ 32 ║
║ 160 ║ 128 ║ 64 ║
║ 288 ║ 256 ║ 128 ║
6 416 ║ 384 ║ 128 ║
║ 544 ║ 512 ║ 128 ║
║ 800 ║ 768 ║ 256 ║
56 1056 ║ 1024 ║ 256 ║
╚═════════════╩════════════════╩═════════════════╝

ВАЖНО: приведенная выше таблица относится только к общей памяти list, За vectorзначения (используемая память, размер структуры) = (48, 1), (48, 8), (48, 16), (48, 32), (80, 64), (80, 72), (112, 96), (128, 120), (176, 168), (272, 264), (544, 528).
Поэтому для других контейнеров необходима другая формула вычисления памяти.

2 ответа

Решение

Помните, что любой механизм выделения общего назначения имеет полезную нагрузку для хранения информации о том, как освободить эту память, как объединить этот буфер со смежными буферами и т. Д. Это происходит с вашей системой malloc (обычно 8-16 дополнительных байтов на распределение плюс дополнительное выравнивание). Распределитель памяти в разделяемой памяти имеет служебную информацию 4-8 байт (в 32-битных системах, 8-16 в 64-битных системах)

Затем библиотека должна хранить количество объектов, чтобы вызывать деструкторы при вызове "destroy_ptr(ptr)" (вы можете выделить массивы, поэтому вам нужно знать, сколько деструкторов должно быть вызвано). И вы сделали именованное распределение, поэтому библиотека должна хранить эту строку в общей памяти и некоторые метаданные для ее поиска (указатель на строку и, возможно, это было "именованное распределение", а не "анонимный" или " распределение экземпляров).

Таким образом, 16 байтов данных + 8 байтов из распределителя памяти + 8 байтов для хранения указателя + метаданных на имя + 12 байтов из строки "TrackOutput" (включая нулевой конец) плюс выравнивание по 8 байтов, вы получите 48 байтов.

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

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

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

Смотрите анализ и сравнения здесь: выбрасывается неверное распределение

Короче говоря: контейнеры на основе узлов здесь не очень хороши.

Подумайте об использовании распределителя пулов - да, таким образом он получает немного громоздкие распределители стеков, но он устраняет большие издержки при распределении узлов.

Вы также можете рассмотреть возможность использования последовательного хранилища, а затем использовать контейнер Boost Intrusive List "поверх этого". Boost Intrusive работает с interprocess::offset_ptr так что вы можете использовать его с управляемыми сегментами памяти.

Другие вопросы по тегам