2010-05-12 2 views
7

У меня есть функция, которая получает входной буфер n байт и нуждается в вспомогательном буфере n байт, чтобы обработать данный входной буфер.Предварительное распределение памяти с C++ в среде реального времени

(я знаю, вектор распределения памяти во время выполнения, скажем, что я использую вектор, который использует статическую выделенную память. Представьте, что это НЕ СТЛ вектор.)

Обычный подход

void processData(vector<T> &vec) { 
    vector<T> &aux = new vector<T>(vec.size()); //dynamically allocate memory 
    // process data 
} 
//usage: 
processData(v) 

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

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

я могу сделать что-то подобное с шаблонной функцией

static void _processData(vector<T> &vec,vector<T> &aux) { 
    // process data 
} 
template<size_t sz> 
void processData(vector<T> &vec) { 
    static aux_buffer[sz]; 
    vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector 
    _processData(vec,aux); 
} 
// usage: 
processData<V_MAX_SIZE>(v); 

Однако рабочей много с шаблонами не очень весело (теперь давайте перекомпилировать все, так как я изменил свой комментарий!), И это заставляет меня сделать некоторую бухгалтерию всякий раз, когда я использую эту функцию.

Есть ли более приятные проекты вокруг этой проблемы?

+1

Обязательный вопрос: вы профилированная свой код, чтобы доказать, что динамический выделение памяти действительно является проблемой? Я знаю, что все говорят, что вы должны выделить все вперёд для вещей в реальном времени, но это действительно зависит от вашей системы. –

+2

В зависимости от требований, то, что я делал в прошлом, динамически выделяет предопределенный размер. Затем сравните требуемый размер с уже выделенным размером и просто перераспределите, если места недостаточно. это гарантирует, что у вас всегда будет достаточно места и что в конечном итоге ассигнования прекратятся, стабилизируя систему. –

+0

@Kristo: «в реальном времени» означает, что это ошибка, если обработка занимает больше времени, чем указанное время. Профилирование может отображать только худший случай, который произошел во время прогона профиля, а не теоретический наихудший случай. Динамическое распределение безопасно только в том случае, если распределитель может гарантировать верхнюю границу времени, которое потребуется. –

ответ

3

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

void processData(vector<T>& vec) 
{ 
    static vector<T> aux(vec.size()); 
    if (vec.size() > aux.size()) { 
     aux.resize(vec.size()); 
    } 
    ... 
} 
+1

Обратите внимание, что это перемещает резервирование памяти от запуска до первого вызова - что может быть уже проблемой.Кроме того, обе версии обычно не являются потокобезопасными (не уверен, что это проблема для OP) – peterchen

+0

Вот почему я упомянул, что это не совсем то, что было задано. Точка в пользу этого решения: он не имеет статического вектора на возможный размер, который может быть пустой тратой. – AProgrammer

0

Не могли бы вы создать небольшую структуру, содержащую ваш вектор и буфер одинакового размера? Тогда у вас будет свой векторный перенос буфера обработки вместе с ним, где бы он ни находился. Если вы передадите его по ссылке или указателю, вам следует избегать накладных расходов на копирование. ПСЕВДОКОД следует ниже:

struct Container 
{ 
    vector<T> vec; 
    vector<T> buf; 

    Container(int size) 
    { 
     vec.reserve(size); 
     buf.reserve(size); 
    } 
}; 

Любая функция, которая в настоящее время принимает ваш вектор аргумент затем взять Container.

1

vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector

Это новое в STL? Или пользовательское расширение?

Типичным решением будет использование пользовательского распределителя. Однако это не обязательно «более симпатично» в коде.

Some intro slides (предупреждение: PowerPoint)
Wikipedia
Google

+0

Цитируя себя: «(Я знаю, что вектор выделяет память во время выполнения, скажем, что я использую вектор, который использует статическую предопределенную память. Представьте, что это НЕ вектор STL.)». –

1

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

+0

Ошибки страницы? В критически важной системе реального времени? Думаю, нет. Где мы будем хранить страницы памяти, если у нас нет диска? Весь код должен соответствовать памяти и оставаться там. Или я чего-то не хватает? –

+0

@ Элазар: вы не сказали, какую ОС вы используете, поэтому я могу предположить, что вы используете «обычную или садовую» ОС, такую ​​как Linux. Если вы используете что-то вроде VxWorks (или, по крайней мере, ОС без VM), то это, конечно, другая история. –

+0

- это ванильный Linux «Общий сад» систем реального времени? Я думаю, что VxWorks заслуживает этого титула больше, чем Linux. –

1

Я думаю, что вы можете предварительно выделить и mlock() достаточно большой пул памяти при запуске, а затем использовать обычные контейнеры STL с распределителями пулов памяти (из FSBA или ваши собственные).

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

0

Возможно, вы можете переопределить новые и удалить операторы, но вам придется управлять всей вашей памятью самостоятельно. Можно выделить столько памяти, сколько вы хотите в начале:

void* operator new (std::size_t size) throw (std::bad_alloc); 
void* operator new[] (std::size_t size) throw (std::bad_alloc); 
void operator delete (void* ptr) throw(); 
void operator delete[] (void* ptr) throw(); 
1

скажет, что я использую вектор который использует статическую выделенную память

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

template<class T, size_t sz> 
class vector 
{ 
enum { size = sz } //either max size 
... 
} 

template<class Vector> 
static void _processData(Vector &vec,Vector &aux) 
{ 
    // process data 
} 
template<class Vector> 
void processData(Vector &vec) { 
    static aux_buffer[Vector::size]; 
    //no need to pass size into constructor, as Vector type knows it already 
    Vector aux(aux_buffer); // use aux_buffer for the vector 
    _processData(vec,aux); 
} 
// usage: 
vector<int, 30> v1; 
vector<float, 40> v2; 
//no need to specify template parameter explicitly 
//every call uses its own function instance and its own buffer of different size 
processData(v1); 
processData(v2); 
1

Это не vector вам нужно беспокоиться о том, что это Распределитель памяти.

Вы можете идеально создать распределитель памяти, который предварительно выделяет свою память, а затем передает его вектору при его создании, вот для чего нужен параметр шаблона Alloc!

И чтобы убедиться, что память не «фактически» выделена, коснитесь ее, когда вы ее выделите.

scoped_array<byte> buffer = new byte[SIZE]; 
memset(buffer.get(), 0, SIZE); 

Теперь вы «просто» должны реализовать пользовательский аллокатор, который относится к этому пулу памяти и передать его в векторе реализацию :)

+0

Как бы пользовательский распределитель знал, сколько раз я его назову? –

+0

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

Смежные вопросы