Почему объект, выделенный в межпроцессной разделяемой памяти 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? Даже если заполнение было выполнено автоматически, это все равно слишком много, чтобы учесть трехкратный размер (для больших структур он увеличивается в 1,33 раза).
Из-за этого я не могу надежно выделять и динамически увеличивать общую память. Если SharedObject содержит динамически растущий список, это может еще больше увеличить неопределенность распределения пространства.

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

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

Обновление:

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

╔═════════════╦════════════════╦═════════════════╗.
║ используемая память ║ размер структуры ║ увеличение объема памяти ║
═══════════════╣
║ 48 ║ 1 ║ ║
║ 48 ║ 4 ║ 0 ║
║ 48 ║ 8 ║ 0 ║
║ 16 ║ 0 ║
║ 64 ║ 32 ║ 16 ║
║ 64 ║ 40 ║ 0 ║
║ 80 ║ 48 ║ 16 ║
║ 96 ║ 64 ║ 32 ║
║ 160 ║ 128 ║ 64 ║
║ 288 ║ 256 ║ 128 ║
║ 3 416 ║ ║ 544 ║ 512 ║ 128 ║
║ 800 ║ 768 ║ 256 ║
║ 1056 ║ 1024 ║ 25 6. ════╝

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


person Nav    schedule 04.06.2015    source источник
comment
Имейте в виду, что если вы выделяете блок, распределителю требуется некоторая память для служебных целей.   -  person marom    schedule 04.06.2015
comment
Но в этом случае дополнительной памяти намного больше, чем можно было бы ожидать для обслуживания.   -  person Nav    schedule 07.06.2015
comment
Неужели никто не может помочь с общей памятью?   -  person Nav    schedule 06.10.2015


Ответы (2)


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

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

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

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

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

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

person igaztanaga    schedule 08.10.2015
comment
Спасибо. По этой логике использование формулы memoryToAllocate = sizeOfStructures + 16 + 16 + stringSize + 16; должно нормально работать для 32-битных и 64-битных систем. Нет ничего плохого в том, чтобы выделить немного больше памяти, чем требуется. - person Nav; 15.10.2015

См. анализ и сравнения здесь: Выброшен неверный alloc

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

Рассмотрите возможность использования распределителя пула — да, он немного громоздкий, но он устраняет большие накладные расходы на выделение узлов.

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

person sehe    schedule 08.10.2015