Количественная оценка производительности сборки мусора и явного управления памятью - PullRequest
19 голосов
/ 06 июня 2010

Я нашел эту статью здесь:

Количественная оценка производительности сборки мусора и явного управления памятью

http://www.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf

В разделе «Вывод» написано:

Сравнение времени выполнения, потребления пространства, и следы виртуальной памяти над Диапазон тестов, мы показываем, что производительность во время выполнения самый эффективный сборщик мусора конкурирует с явной памятью управление, когда дано достаточно памяти. В частности, при сборке мусора имеет в пять раз больше памяти, чем требуется, его производительность во время выполнения соответствует или немного превышает явное управление памятью. Тем не мение, производительность сборки мусора существенно ухудшается, когда это должно используйте кучи меньшего размера. При трехкратном увеличении много памяти, на 17% медленнее средний и с вдвое большим памяти, он работает на 70% медленнее. Мусор коллекция также более восприимчива к пейджинг, когда физической памяти мало. В таких условиях весь мусор Коллекционеры, которых мы здесь исследуем, страдают производительность по порядку величины штрафы относительно явной памяти управление.

Итак, если мое понимание верно: если у меня есть приложение, написанное на нативном C ++, требующее 100 МБ памяти, для достижения той же производительности с помощью "управляемого" (т.е. основанного на сборщике мусора) языка (например, Java, C #), приложение должно требовать 5 * 100 МБ = 500 МБ? (А при 2 * 100 МБ = 200 МБ управляемое приложение будет работать на 70% медленнее, чем собственное приложение?)

Знаете ли вы, если текущие (то есть последние Java VM и .NET 4.0) сборщики мусора испытывают те же проблемы, что описаны в вышеупомянутой статье? Повысилась ли производительность современных сборщиков мусора?

Спасибо.

Ответы [ 6 ]

10 голосов
/ 06 июня 2010

если у меня есть приложение, написанное на родном C ++ требуется 100 МБ памяти для достижения та же производительность с «управляемым» (т.е. на основе сборщика мусора) язык (например, Java, C #), приложение должно потребоваться 5 * 100 МБ = 500 МБ? (А также с 2 * 100 МБ = 200 МБ, управляемый приложение будет работать на 70% медленнее, чем родное приложение?)

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

8 голосов
/ 06 июня 2010

Вы, кажется, спрашиваете о двух вещах:

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

Ответ на первый вопрос заключается в том, что в алгоритмах GC не было серьезных прорывов, которые могли бы сделать недействительными общие выводы:

  • Для управления памятью в GC по-прежнему требуется значительно больше виртуальной памяти.
  • Если вы попытаетесь ограничить размер кучи, производительность GC значительно снизится.
  • Если реальная память ограничена, подход к управлению памятью с помощью GC приводит к существенно худшей производительности из-за накладных расходов на подкачку.

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

  • Первоначальное исследование было выполнено с JikesRVM, а не с Sun JVM.
  • Сборщики мусора Sun JVM улучшились за ~ 5 лет после исследования.
  • В исследовании не учитывается, что структуры данных Java занимают больше места, чем эквивалентные структуры данных C ++, по причинам, не связанным с GC.

В последнем пункте я видел презентацию того, кто говорит о накладных расходах памяти Java. Например, он обнаружил, что минимальный размер представления строки Java составляет примерно 48 байтов. (Строка состоит из двух примитивных объектов: один объект с четырьмя полями размером в слово и другой массив с минимум одним словом содержимого. Каждый примитивный объект также имеет 3 или 4 слова служебных данных.) Структуры данных коллекции Java аналогично использовать гораздо больше памяти, чем думают люди.

Эти накладные расходы не связаны с GC per se . Скорее они являются прямыми и косвенными последствиями проектных решений на языке Java, JVM и библиотеках классов. Например:

  • Каждый заголовок примитивного объекта Java 1 резервирует одно слово для значения «хэш-кода объекта» и одно или несколько слов для представления блокировки объекта.
  • Представление String должно использовать отдельный «массив символов» из-за ограничений JVM. Два из трех других полей являются попыткой сделать операцию substring менее требовательной к памяти.
  • Типы коллекций Java используют много памяти, поскольку элементы коллекции не могут быть напрямую связаны. Так, например, издержки (гипотетического) односвязного класса коллекции списков в Java будут составлять 6 слов на элемент списка. Напротив, оптимальный связанный список C / C ++ (т. Е. Каждый элемент имеет указатель «next») имеет издержки, равные одному слову на элемент списка.

1 - Фактически, накладные расходы в среднем меньше, чем это. JVM только «раздувает» блокировку после использования и конфликта, и аналогичные приемы используются для хэш-кода идентификатора. Фиксированные накладные расходы составляют всего несколько битов. Тем не менее, эти биты складываются в значительно больший заголовок объекта ... который является реальной точкой здесь.

4 голосов
/ 06 июня 2010

Майкл Боргвардт прав в том, что приложение ограничено в распределении памяти. Это согласно закону Амдаля.

Однако я использовал C ++, Java и VB .NET. В C ++ доступны мощные методы, которые выделяют память в стеке, а не в куче. Распределение стека легко в сотни раз быстрее, чем выделение кучи. Я бы сказал, что использование этих методов может удалить, возможно, одно выделение из восьми, а использование записываемых строк - одно выделение из четырех.

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

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

* 1008. 1009 *
3 голосов
/ 06 июня 2010

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

Кстати: в C ++ вы можете сделать множество тиков, поддерживающих эту возможность, которые недоступны в Java.

Если вас беспокоит стоимость GC или создания объектов, вы можете взятьшаги, чтобы минимизировать, сколько объектов вы создаете.Обычно это хорошая идея, когда производительность критична для любого языка.

Стоимость основной памяти не так важна, как раньше.Машина с 48 ГБ относительно дешева в наши дни.8-ядерный сервер с 48 ГБ основной памяти можно арендовать за 9 фунтов стерлингов в день.Попробуйте нанять разработчика за 9 фунтов в день.;) Тем не менее, что все еще относительно дорого, так это кэш-память процессора.Довольно сложно найти систему с более чем 16 МБ кэш-памяти процессора.ср 48 000 МБ основной памятиСистема работает намного лучше, когда приложение использует кэш процессора, и это объем памяти, который необходимо учитывать, если производительность критична.

3 голосов
/ 06 июня 2010

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

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

Короче говоря, оба, несомненно, улучшились, по крайней мере, немного, но ни в одном случае не было серьезных новых алгоритмов, которые изменяют фундаментальный ландшафт. Сомнительно, что текущие реализации дадут разницу ровно в 17%, как указано в статье, но есть довольно хороший шанс, что если вы повторите тесты сегодня, вы все равно получите разницу где-то в 15-20% или около того. Различия между тогда и сейчас, вероятно, меньше , чем различия между некоторыми из различных алгоритмов, которые они тестировали в то время.

0 голосов
/ 26 февраля 2019

Первое замечание: теперь 2019 год, и многое улучшилось.Пока вы не запускаете GC, выделение будет таким же простым, как увеличение указателя.В C ++ это намного больше, если вы не реализуете свой собственный механизм для выделения в блоках.И если вы используете интеллектуальные общие указатели, каждое изменение счетчика ссылок потребует заблокированного приращения (инструкция xaddl) само по себе медленно и требует, чтобы процессоры обменивались данными для аннулирования и повторной синхронизации своей кеш-линии.Более того, с GC вы получаете больше локальности по крайней мере тремя способами.Сначала, когда он выделяет новый сегмент, он обнуляет память и нагревает кеш-линии.Во-вторых, он сжимает кучу и заставляет данные оставаться ближе друг к другу, и, наконец, все потоки используют свою собственную кучу.В заключение, хотя его сложно протестировать и сравнить с каждым сценарием и реализацией GC, я где-то прочитал на SO, что его проверенный GC работает лучше, чем ручное управление памятью.

...