Распределение памяти/освобождение Узкое место?

Насколько узким местом является распределение/освобождение памяти в типичных программах реального мира? Ответы любого типа программ, в которых производительность обычно имеет значение, приветствуются. Являются ли приличные реализации коллекции malloc/free/garbage достаточно быстрыми, чтобы это было лишь узким местом в нескольких случаях с углом зрения, или же самое важное преимущество для критически важных программных средств существенно повлияло на попытку уменьшить объем выделения памяти или иметь более быстрый malloc/free/сборка мусора?

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

Изменить: хотя я упоминаю malloc, этот вопрос не предназначен для специфики C/С++.

Ответ 1

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

В играх, которые я видел, звонки в malloc()/free() потребляют до 15% CPU (в плохо написанных продуктах) или с тщательно написанными и оптимизированными блочными распределителями всего 5%. Учитывая, что игра должна иметь согласованную пропускную способность в шестьдесят герц, если она останавливается на 500 мс, а сборщик мусора иногда не практичен.

Ответ 2

Почти каждое высокопроизводительное приложение теперь должно использовать потоки для использования параллельных вычислений. В этом случае при создании приложений на C/С++ возникает реальный убийца скорости выделения памяти.

В приложении C или С++ malloc/new должен блокировать глобальную кучу для каждой операции. Даже без конкуренции блокировки далеки от свободы и их следует избегать как можно больше.

Java и С# лучше подходят для этого, поскольку потоки были разработаны с самого начала, а распределители памяти работают из пулов потоков. Это также можно сделать в C/С++, но оно не является автоматическим.

Ответ 3

Во-первых, поскольку вы сказали malloc, я предполагаю, что вы говорите о C или С++.

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

Типичным обходным решением является выделение как можно большего количества памяти спереди и зависание, пока ваша программа не выйдет. Вы можете использовать эту память для хранения больших монолитных наборов данных или использовать реализацию пула памяти, чтобы использовать ее в кусках. Многие стандартные реализаций библиотек C/С++ делают определенную сумму памяти, объединив себя именно по этой причине.

Нет двух способов, но если у вас есть программа C/С++, зависящая от времени, выполнение большого объема выделения/освобождения памяти приведет к урон производительности.

Ответ 4

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

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

В Java и JVM-языках новые объекты теперь очень, очень и очень быстрые.

Здесь одна достойная статья того парня, который знает его материал с некоторыми ссылками внизу на более связанные ссылки: http://www.ibm.com/developerworks/java/library/j-jtp09275.html

Ответ 5

В Java (и, возможно, на других языках с достойной реализацией GC) выделение объекта очень дешево. В SUN JVM требуется всего 10 циклов ЦП. Malloc в C/С++ намного дороже, просто потому, что он должен делать больше работы.

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

Ответ 6

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

Эта статья была написана в 2005 году, а управление памятью в стиле JVM уже было впереди. С тех пор ситуация улучшилась.

Какой язык может похвастаться быстрее производительность распределения, Java языка или C/С++? Ответ может удивить вас - выделение в современных JVM намного быстрее, чем лучшие выполнение реализаций malloc. общий путь кода для нового объекта() в HotSpot 1.4.2 и более поздние версии приблизительно 10 машинных инструкций (данные предоставлены Sun, см. раздел "Ресурсы" ) тогда как наиболее эффективный malloc реализации в C требуют в среднем от 60 до 100 инструкции на вызов (Detlefs, et. и др.; см. Ресурсы). И распределение производительность не является тривиальным компонентом общей производительности - контрольные показатели показывают, что многие реальные C и С++ программ, таких как Perl и Ghostscript, потратить от 20 до 30 процентов их общее время выполнения в malloc и бесплатно - гораздо больше, чем сбор и сбор мусора накладные расходы на здоровую Java приложение.

Ответ 7

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

С другой стороны, почти все современные языки программирования скрывают эти операции за "распределителями", которые работают с заранее выделенными буферами.

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

Ответ 8

Здесь используется система распределения памяти c/С++. Стратегия распределения по умолчанию в большинстве случаев подходит для большинства случаев, но ее можно изменить в соответствии с тем, что необходимо. В системах ГХ не так много можно сделать, чтобы изменить стратегии распределения. Конечно, есть цена, которую нужно заплатить, и что нужно отслеживать распределения и бесплатно их правильно. С++ делает это дополнительно, и стратегия распределения может быть указана для каждого класса с использованием нового оператора:

class AClass
{
public:
  void *operator new (size_t size); // this will be called whenever there a new AClass
   void *operator new [] (size_t size); // this will be called whenever there a new AClass []
  void operator delete (void *memory); // if you define new, you really need to define delete as well
  void operator delete [] (void *memory);define delete as well
};

Многие из шаблонов STL также позволяют определять пользовательские распределители.

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

Ответ 9

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

Чтобы говорить с вами напрямую, если я правильно понимаю, критерием эффективности использования является пропускная способность.

Это для меня означает, что вы должны выглядеть почти эксклюзивно на NUMA распределители.

Ни одна из ранних ссылок; Документ IBM JVM, Microquill C, SUN JVM. Поощряйте этот момент, поэтому я очень подозреваю их применение сегодня, где, по крайней мере, на AMD ABI, NUMA является выдающимся контроллером памяти-процессора.

Руки вниз; реальный мир, фальшивый мир, какой бы мир... NUMA не знает, какие запросы/технологии использования памяти быстрее. К сожалению, сейчас я запускаю Windows, и я не нашел "numastat", который доступен в Linux.

A friend моего написан о это в глубине в его внедрении для ядра FreeBSD.

Не смотря на то, что я могу показать на специальном, обычно ОЧЕНЬ большое количество локальных запросов памяти node поверх удаленного node (подчеркивая очевидную производительность пропускной способности), вы вы можете испытать уверенность в себе, и это, вероятно, будет тем, что вам нужно, поскольку ваша производительность charicterisitc будет очень специфичной.

Я знаю, что во многих отношениях, по крайней мере, более раннее 5.x VMWARE довольно слабое, хотя бы в то время, за то, что не воспользовалось NUMA, часто требовало страниц с удаленного node. Тем не менее, VM - очень уникальный зверь, когда дело доходит до отсечения памяти или контейнеризации.

Одна из ссылок, которые я привел, касается внедрения Microsoft API для AMD ABI, которая имеет специализированные интерфейсы распределения NUMA для разработчиков пользовательских приложений для пользователей;)

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

Они в какой-то мере покрывают количественно, по крайней мере, для их использования, то, что точный компромисс между пространством/временем, как правило, они идентифицировали LFH (oh ya и, кстати, LFH - это просто режим, по-видимому, стандартная куча) или аналогично разработанный подход по существу потребляет значительно больше памяти с летучей мыши, но со временем может закончиться использованием меньшего объема памяти... grafix также опрятно...

Я бы подумал, однако, что выбор внедрения HEAP на основе вашей типичной рабочей нагрузки после того, как вы ее хорошо понимаете;) - хорошая идея, но для того, чтобы хорошо понимать ваши потребности, сначала убедитесь, что ваши основные операции правильные, прежде чем оптимизировать эти коэффициенты и end;)

Ответ 10

Согласно Техническая спецификация MicroQuill SmartHeap, "типичное приложение [...] тратит 40% своего общего времени на управление памятью". Вы можете принять эту цифру как верхнюю границу, я лично считаю, что типичное приложение тратит больше 10-15% времени выполнения, выделяя/освобождая память. Это редко является узким местом в однопоточном приложении.

В многопоточных приложениях C/С++ стандартные распределители становятся проблемой из-за конфликта блокировок. Здесь вы начинаете искать более масштабируемые решения. Но имейте в виду Закон Amdahl.

Ответ 11

Другие рассмотрели C/С++, поэтому я просто добавлю небольшую информацию о .NET.

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

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

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

Ответ 12

Практически все вы выключены, если вы говорите о куче Microsoft. Синхронизация без труда обрабатывается, как и фрагментация.

Текущая perferrred heap - это LFH, ( LOW FRAGMENTATION HEAP), по умолчанию она находится в Vista + OS и может быть настроена на XP через gflag с большой неприятностью

Легко избежать любых проблем с блокировкой/блокировкой/конфликтом/шинами, а также с

HEAP_NO_SERIALIZE

во время HeapAlloc или HeapCreate. Это позволит вам создавать/использовать кучу, не вступая в блокированное ожидание.

Я бы рекомендовал создать несколько куч с помощью HeapCreate и определить макрос, возможно, mallocx (enum my_heaps_set, size_t);

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

Куча * api внутренне называется malloc/new.

Здесь представлена ​​хорошая статья о некотором динамическом управлении памятью, а некоторые даже приятнее . Инструмент и анализ активности кучи.