Перезапись против распределения / освобождения - эффективность - PullRequest
4 голосов
/ 02 июня 2009

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

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

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

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

Ответы [ 8 ]

16 голосов
/ 02 июня 2009

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

6 голосов
/ 02 июня 2009

Хотя я не могу дать вам научное «подтверждение», подумайте:

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

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

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

4 голосов
/ 02 июня 2009

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

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

2 голосов
/ 02 июня 2009

Относительно Java:

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

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

1 голос
/ 02 июня 2009

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

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

1 голос
/ 02 июня 2009

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

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

1 голос
/ 02 июня 2009

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

0 голосов
/ 26 августа 2009

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

Вот как выглядит новое и удаленное в 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. Это может помочь вам, оптимизируя некоторые операции.

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