Когда сбор мусора может быть быстрее, чем ручное управление памятью?
При каких обстоятельствах сбор мусора эффективнее ручного управления памятью? (Здесь руководство может означать использование malloc и бесплатно, как в C, или чистые методы RAII и интеллектуального указателя, популяризированные С++)
Мне нравится, как сборка мусора может устранить некоторую случайную сложность при написании программного обеспечения, но я был еще более доволен тем, как RAII и интеллектуальные указатели могут устранить эту сложность, а также работать с ресурсами, отличными от памяти, будучи детерминированными и обеспечивая гарантии производительности и будучи более эффективными в целом. Поэтому я подумал, что могу смело игнорировать сбор мусора. Тем не менее, я заметил, что люди говорят, что сбор мусора быстрее, чем жесткое управление ресурсами, используемое на С++, например, когда доступно много дополнительной памяти.
Итак, когда именно сбор мусора превосходит ручное управление памятью? Мне нравятся RAII и интеллектуальные указатели, но с удовольствием принимаю сборку мусора в качестве другого инструмента, если это быстрее.
Ответы
Ответ 1
Никогда, и я могу это доказать.
Во-первых, предположим, что мы используем лучшие алгоритмы в любом случае. Использование субоптимального алгоритма может доказать что угодно.
Во-вторых, предположим, что лучший алгоритм GC использует времена T0...Tn
, чтобы решить, должно ли освобождение памяти i
быть освобождено в определенный момент. Тогда общее число Σ(Ti)
Теперь существует эквивалентный алгоритм управления ручным управлением памятью, который использует один и тот же алгоритм GC, но учитывает только блоки памяти, которые были отмечены вручную для освобождения. Поэтому время работы Σ(δiTi)
(при δi = 0, когда блок я не был освобожден, и = 1, когда он был).
Очевидно, Σ(δiTi) ≤ Σ(Ti)
: есть ручной алгоритм, который строго не хуже алгоритма GC.
Как это контрастирует с другими ответами?
- "С RAII вам нужно освобождать каждый раз, когда вы выделяете". - Нет, это ошибочное предположение. Мы должны сравнивать либо сгруппированные, либо без пакета операции. GC будет намного хуже, если вы также выполняете GC-сессию каждый раз, когда что-то выходит из сферы действия.
- "Улучшенная локальность" - ну, если вы не снижаете тот факт, что языки, отличные от GC, могут использовать стек гораздо чаще, и этот стек имеет отличную локальность ссылок.
- "низкие коэффициенты утечек" - это полностью верно, но мы обсуждали производительность во время выполнения. (Кстати: хорошо указать, что это низкие, но отличные от нуля).
Ответ 2
Преимущества GC:
- GC выделяется просто путем увеличения указателя, кучи-распределители должны принимать контрмеры, чтобы избежать фрагментации кучи
- GC улучшает локальность кэша процессора, большое дело на современных процессорах.
- GC не требует дополнительного кода для выпуска памяти, низких коэффициентов утечки.
- поколение GG может собирать мусор одновременно, делая управление памятью максимально свободным на многоядерном процессоре.
Недостатки GC:
- трудно сделать эффективным на языке, который рассматривает указатели как типы первого класса
- использует больше виртуального пространства памяти из-за латентности сборки
- нечеткая абстракция для ресурсов операционной системы, отличных от памяти
- может вызвать паузы в работе программы в некоторых случаях, в то время как сбор мусора.
Это безумие для перфоманса, GC без проблем обойдется в распределитель кучи. Конкурс китайских программистов по программированию между Рико Мариани и Раймондом Ченом часто цитируется, обзор здесь. Программа Raymond С++ в конечном итоге выиграла, но только после ее перезаписи несколько раз и отказалась от стандартной библиотеки С++.
Ответ 3
Существует один конкретный сценарий, в котором указатель GC намного быстрее, чем интеллектуальный указатель (указатель на подсчет ссылок), когда оба реализованы в традиционном С++ (т.е. не С++/CLR, поскольку у нас не будет такой же роскоши, как Compact память после Mark-Sweep, и мы пытаемся сравнить яблоки с яблоками здесь, насколько мы можем), предполагая, что GC использует один и тот же основной менеджер памяти кучи. Это когда время назначения объекта значительно перегружает время создания объекта.
Примеры включают сортировку, разворот массива и т.д.
См. здесь больше информации о моем тесте с каркасом GC, который я реализовал с использованием традиционных ссылок С++ и ссылок на ссылки. Результатом теста разворота массива был GcString, который был в 16,5 раз быстрее, чем ref counted.
Это можно отнести к мучительно медленной семантике блокировки шины в указателе с подсчетом ссылок (если вы не нацеливаете только однопоточные приложения, для обеспечения безопасности потоков необходимы блокировки). По моему опыту внедрения высокопроизводительного точного GC в традиционном С++, я могу сказать, что есть больше возможностей для оптимизации с помощью руководства GC vs 'manual' (в соответствии с определением ОП ручного управления) (т.е. Интеллектуальными указателями).