2009-06-02 3 views
4

Я пишу приложение C++, для которого требуется блок памяти (около 1000 байт) в качестве временного буфера для некоторой обработки текста. Операция может повторяться до 10000 раз в секунду.Overwriting vs allocation/deallocation - efficiency

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

Это звучит как здравый смысл для C++, но я просто не могу найти что-либо в Интернете, что подтверждает его.

Является ли ситуация иной для компьютерных языков с автоматическими средствами сбора мусора (например, Java, .net)?

+0

Статический буфер, очевидно, !!! Если вам нужно обнулить всю память, используйте memset :) – toto

ответ

16

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

+3

, поддерживая 10 000 распределений в секунду, как будто это может иметь значение - если 9999 итераций за одну секунду слишком мало ... если есть ограничения в реальном времени, построение доказуемых ограничений в дизайне может потребоваться. Не вся «оптимизация» времени разработки является преждевременной. –

+1

Я думаю, что Крис ударил ноготь по голове. Чтобы гарантировать пропускную способность, вы должны тщательно изучить алгоритмическую сложность кода. Статические и локальные распределения имеют предсказуемую производительность. Это не относится к динамическим распределениям. – Constantin

6

Пока я не могу дать вам научное «подтверждение», считают это:

Всякий раз, когда процессор выполняет инструкцию, это требует времени. Если вы сбросите свой буфер и затем перераспределите его, CPU должен будет выполнить инструкции deallocate/reallocate. Он не должен выполнять эти операции, если вы повторно используете буфер.

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

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

1

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

4

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

calloc делает весь буфер равным 0, но я не могу представить, чтобы он был заметно быстрее, чем malloc + memset.

2

Что касается Java:

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

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

+0

Спасибо.Я задал вопрос Java, потому что знаю, что у вас нет контроля над освобожденной памятью. Тем не менее, вы правильно указываете, что любая выделенная память будет заполнена нулями, в отличие от C++, когда вам нужно сделать очистку самостоятельно. – Andy

1

Я собираюсь пропустить стандарт «не волнуйтесь об оптимизации», поскольку все остальные его охватили. Будет самым быстрым объявить ваш буфер как локальный для вашей функции и использовать memset для 0. С помощью локального буфера пространство «выделяется» компилятором, просто перемещая указатель стека на правильное количество байтов. Распределение не происходит быстрее. С визуальной студией вы можете добавить #pragma intrinsic(memset). Я знаю, что gcc поддерживает intrinsics тоже, но я не помню, как сказать ему использовать их. Я думаю, что последние версии будут использовать их там, где это возможно, без ведома. Внутренний memset расширяется до нескольких встроенных инструкций, которые говорят процессору о диапазоне памяти. Вы не получите быстрее. Тем не менее, не обнуляйте память, если вам это не нужно.

Кроме того, ваш код будет намного яснее, просто используя локально объявленный буфер. Из того, что вы сказали, ваш буфер не должен оставаться за пределами области действия, которая будет использовать его. 1000 байтов в современных условиях крошечные. Использование динамической памяти вместо автоматической памяти добавит кучу кода, который необходимо протестировать и поддерживать. Не делай этого. Автоматическая память - очевидный выбор.

+0

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

+0

Правда. Размер стека по умолчанию для визуальной студии - всего 1 М. –

1

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

Использование одного фиксированного буфера неоднократно будет предлагать на порядок больше производительности: - особенно это среда сбора мусора, в которой неиспользуемые блоки памяти могут задерживаться на фристайле до тех пор, пока не будет запущен цикл сбора мусора.

+0

На самом деле, могут быть гарантии. Нет гарантии O (1), что вы получаете при повторном использовании. –

+0

Я действительно хотел бы найти онлайн-анализ эффективности бесплатного алгоритма хранилища. –

0

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

Вот как новый и удалить внешний вид под капотом в C++:

#include <cstdlib> 
using std::malloc; 
using std::free; 
#include <new> 
using std::bad_alloc; 

void * operator new(size_t n) 
{ 
    void * p = malloc(n); 
    if(!p) throw bad_alloc(); 
    return p; 
} 

void operator delete (void *p) 
{ 
    if (p) free(p); 
} 

Делая новый и удалить все время может быть дорогостоящим! Вот почему такие языки, как C# и Java, медленнее C++. Единственным преимуществом сборщика мусора является то, что он объединяет все в памяти (дефрагментирует память) для вашей программы. Это может быть дорогостоящим, если у вас есть тонна вещей в памяти для вашей программы.

Также посмотрите алгоритм в STL. Это может помочь вам, оптимизируя некоторые операции.

+0

Почему это не помогло? – Partial