методы std :: list, std :: vector и malloc () - PullRequest
3 голосов
/ 19 июля 2009

Работа с типами stl: list и stl :: vector в обработчиках прерываний. Я хочу избежать вызовов malloc ().

Вопрос: Каков наилучший способ предотвращения вызовов malloc () в списке STL и в векторе? Достаточно ли создать структуру с заранее заданным размером, а затем избегать вызовов push / pop / erase?

Заранее спасибо

Ответы [ 6 ]

14 голосов
/ 19 июля 2009
Контейнеры

STL, такие как std::list и std::vector, имеют конструкторы, принимающие тип Allocator. Предоставляя свой собственный распределитель вместо использования по умолчанию, вы можете контролировать, как контейнер распределяет память. Эта опция используется редко, но использование вашего собственного распределителя в среде реального времени является хорошим примером того, где эта функция полезна (и доказывает, что разработчики STL проделали очень хорошую работу).

Требования к пользовательскому типу распределителя описаны в 20.1.6 в стандарте C ++

7 голосов
/ 19 июля 2009

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

Вы можете предварительно выделить память для std::vector, вызвав метод reserve(). Такие методы, как push_back(), pop(), insert() и erase(), управляют размером вектора (количеством элементов, которые он содержит в настоящее время). Они влияют на емкость (количество элементов, для которых у нее есть место), когда новый размер больше текущей емкости. reserve(x) гарантирует, что емкость больше или равна x, увеличивая емкость при необходимости. (Также обратите внимание, что единственной операцией, которая когда-либо уменьшает емкость вектора, является swap(), поэтому вам не нужно беспокоиться о erase() уменьшении емкости вектора.)

Этот подход не будет работать для std::list, но есть другой подход, который будет: предварительно выделять элементы списка, вставляя их в «запасной» список. Вместо вставки новых элементов используйте метод splice(), чтобы переместить их из «запасного» списка в «основной» список. Вместо стирания элементов используйте метод splice(), чтобы переместить их из «основного» списка в «запасной» список.

4 голосов
/ 19 июля 2009

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

  • пользовательские распределители : для нашей системы отслеживания утечек памяти, нашего инструментального профилировщика и некоторых других систем мы предварительно выделяем и / или "пул" (см., Например, boost :: pool) выделяет, используя предоставленный Allocator - обычно для std :: set или std :: map, но принцип тот же для std :: list.
  • резервирование / изменение размера : для std :: vectors очень распространено для нас резервирование или изменение размера (разница важна, но оба могут помочь избежать распределения в будущем) заранее.

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

Обычно, однако, мы избегаем проблем с прерываниями и распределением другими способами:

  • войти / выйти : стараться не делать ничего, кроме установки флагов или тривиальных копий во время прерываний; Иногда статический (или предварительно выделенный) буфер является гораздо лучшим решением, чем контейнер STL. Слишком долгое удержание прерываний обычно является причиной катастрофы.
  • отключение прерываний во время выделения / освобождения : прерывания ставятся в очередь, пока мы распределяем / освобождаем, а не отправляются немедленно - это особенность процессора, с которым мы работаем. В сочетании с политикой выборочного увеличения объема этого отключения / постановки в очередь (например, с помощью манипуляции std :: list), мы можем иногда обходиться без модели обработчика прерываний как производитель, все остальное как потребитель, не переопределяя распределитель. Если мы находимся в процессе потребления чего-либо из std :: list (например, сообщения, полученного от сетевого оборудования), прерывания ставятся в очередь на максимально короткий период времени, пока выскакивает копия того, что мы собираемся обработать .

Обратите внимание, что структуры данных без блокировки могут быть альтернативой второму пункту здесь, мы не настроили и не выполнили профилирование, чтобы посмотреть, поможет ли это. В любом случае, разработка собственного - это непростое дело.

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

3 голосов
/ 19 июля 2009

Еще одна вещь, которую нужно добавить: const std::vector не приведет к выделению ресурсов. Поэтому, если ваш код обработки прерываний не меняет вектор, объявите его const, и компилятор заставит vector остаться прежним.

2 голосов
/ 19 июля 2009

Как упомянул onebyone.livejournal.com , стандарт C ++ ничего не говорит об обработчиках прерываний . Он говорит об обработчиках signal , но даже тогда это очень серая область. Единственное, что вы можете сделать внутри обработчика сигнала, который гарантированно будет иметь четко определенное поведение во всех соответствующих реализациях C / C ++, - это присвоить переменную типа sig_atomic_t и вернуть, например ::

sig_atomic_t flag = 0;

// This signal handler has well-defined behavior
void my_signal_handler(int signum)
{
    flag = 1;
}

int main(void)
{
    signal(SIGINT, &my_signal_handler);

    while(1)
    {
        doStuff();
        if(flag)
        {
            flag = 0;
            actuallyHandleSignalNow();
        }
    }

    return 0;
}

Хотя на практике вы почти всегда можете сделать немного больше в своем обработчике сигналов.

1 голос
/ 19 июля 2009

Для std::vector этого должно быть достаточно. Я не думаю, что что-нибудь гарантирует это все же. Распределение памяти считается деталью реализации. Если вы можете ограничить себя определенным размером, я предлагаю вместо этого использовать простой статический массив. Таким образом, вы имеете точный контроль над тем, что именно происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...