Подтвердить что ты не робот

К GC или не к GC

Недавно я увидел два действительно приятных и обучающих языка:

Этот первый от Herb Sutter представляет все приятные и интересные функции С++ 0x, почему будущее С++ кажется ярче, чем когда-либо, и как M $считается хорошим парнем в этой игре. Разговор связан с эффективностью и тем, что минимизация активности кучи очень часто повышает производительность.

Этот другой, автор Andrei Alexandrescu, мотивирует переход от C/С++ к его новому игровому чейнджеру D. Большая часть материала D кажется действительно хорошо мотивированы и разработаны. Одна вещь, однако, удивила меня, а именно, что D толкает сборку мусора и что все классы создаются исключительно по ссылке. Еще более запутанным является то, что в книге "Руководство по программированию языка программирования D" в разделе "Управление ресурсами" говорится следующее:

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

Это противоречит постоянному разговору Саттера о минимизации активности кучи. Я очень уважаю идеи Саттера и Александреску, поэтому я немного смущен этими двумя ключевыми вопросами.

  • Не создает экземпляры экземпляра исключительно по результату ссылки во множестве ненужной активности кучи?

  • В каких случаях мы можем использовать сборку мусора без ущерба для производительности во время выполнения?

4b9b3361

Ответ 1

Чтобы ответить на два вопроса:

  • Да, создание экземпляров класса по ссылке приводит к большому количеству операций кучи, , но:

    а. В D у вас есть struct, а также class. A struct имеет семантику значений и может делать все, что может класс, кроме полиморфизма.

    б. Полиморфизм и семантика значений никогда не работали хорошо вместе из-за проблемы срезания.

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

  • GC может быть сопоставим или быстрее, чем ручное управление памятью, если:

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

    б. У вас есть топ-оф-лайн сборщик мусора (D текущая реализация GC, по общему признанию, несколько наивна, хотя в последних нескольких версиях он видел некоторые крупные оптимизации, поэтому это не так плохо, как было).

    с. Вы выделяете в основном небольшие объекты. Если вы выделяете в основном большие массивы и производительность заканчивается проблемой, вы можете захотеть переключить некоторые из них на кучу C (у вас есть доступ к C malloc и свободный в D), или, если он имеет ограниченное время жизни, некоторые другие распределитель вроде RegionAllocator. (RegionAllocator в настоящее время обсуждается и уточняется для возможного включения в стандартную библиотеку D).

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

Ответ 2

Причина создания объекта в куче медленнее, чем его создание в стеке, заключается в том, что методы выделения памяти должны иметь дело с такими вещами, как фрагментация кучи. Выделение памяти в стеке так же просто, как увеличение указателя стека (операция с постоянным временем).

Тем не менее, с уплотняющим сборщиком мусора вам не нужно беспокоиться о фрагментации кучи, распределение кучи может быть таким же быстрым, как распределение стека. Страница "Коллекция мусора" для языка программирования D объясняет это более подробно.

Утверждение, что языки GC'd работают быстрее, вероятно, предполагает, что многие программы выделяют память в куче гораздо чаще, чем в стеке. Предполагая, что распределение кучи может быть быстрее на языке GC'd, тогда следует, что вы только что оптимизировали огромную часть большинства программ (выделение кучи).

Ответ 3

Ответ на 1):

Пока ваша куча непрерывна, выделение на нее столь же дешево, как выделение в стеке.

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

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

Что хорошая новость:)

Ответ на 2):

Технология GC значительно продвинулась вперед; они даже приходят в настоящее время ароматы в настоящее время. Это означает, что гарантирование непрерывной памяти - это зависящая от политики проблема, зависящая от реализации.

Итак, если

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

Вы можете получить лучшую производительность.

Отвечайте на вопрос, который не задан:

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

Ответ 4

Это не "рукописный" код "сборщик мусора" или "утомительная ошибка, подверженная ошибкам". Умные указатели, которые действительно умны, могут дать вам семантику стека и означать, что вы никогда не набираете "delete", но вы не платите за сбор мусора. Здесь другое видео Herb, которое делает точную и быструю - то, что мы хотим.

Ответ 5

Еще один момент для рассмотрения - правило 80:20. Вероятно, что подавляющее большинство мест, которые вы выделяете, не имеют отношения к делу, и вы не получите много денег за GC, даже если вы можете подтолкнуть стоимость к нулю. Если вы согласитесь с этим, то простота, которую вы можете получить с помощью GC, может сместить затраты на ее использование. Это особенно верно, если вы можете избежать копирования. Что D обеспечивает GC для 80% случаев и доступ к распределению стека и malloc для 20%.

Ответ 6

Даже если у вас идеальный сборщик мусора, он все равно был бы медленнее, чем создание вещей в стеке. Таким образом, вы должны иметь язык, который позволяет одновременно. Кроме того, единственный способ достичь такой же производительности с сборщиком мусора, как и с распределением управляемых вручную вручную (сделано правильно), заключается в том, чтобы сделать то же самое с памятью, как это сделал опытный разработчик, и что во многих случаях требуют принятия решений сборщика мусора во время компиляции и выполнения во время выполнения. Обычно сборка мусора делает вещи медленнее, языки, работающие с динамической памятью, медленнее, а предсказуемость выполнения программ, написанных на этих языках, низкая, а латентность исполнения выше. Честно говоря, я лично не понимаю, зачем нужен сборщик мусора. Управление памятью вручную не сложно. По крайней мере, не в С++. Конечно, я не буду возражать против компилятора, генерирующего код, который очищает все для меня, как я бы это сделал, но в настоящее время это кажется невозможным.

Ответ 7

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

Достойный компилятор почти наверняка сделает x выделение стека в следующем примере:

void f() {
    Foo* x = new Foo();
    x->doStuff(); // Assuming doStuff doesn't assign 'this' anywhere
    // delete x or assume the GC gets it
}

Что делает компилятор, называется escape-анализ.

Кроме того, D теоретически может иметь движущийся GC, что означает потенциальные улучшения производительности за счет улучшения использования кеша, когда GC сжимает ваши объекты кучи вместе, Он также борется с фрагментацией кучи, как объясняется в ответе Джека Эдмондса. Аналогичные вещи можно сделать с помощью ручного управления памятью, но это дополнительная работа.

Ответ 8

Инкрементный низкоприоритетный GC будет собирать мусор, если задача с высоким приоритетом не запущена. Высокоприоритетные потоки будут работать быстрее, так как освобождение памяти не будет выполнено. Это идея GC GC Henriksson RT см. http://www.oracle.com/technetwork/articles/javase/index-138577.html

Ответ 9

Сбор мусора действительно замедляет работу кода. Это добавляет дополнительную функциональность в программу, которая должна работать в дополнение к вашему коду. Есть и другие проблемы с ним, такие как, например, GC не работает, пока на самом деле не требуется память. Это может привести к небольшим утечкам памяти. Другая проблема заключается в том, что если ссылка не удалена должным образом, GC не подберет ее и еще раз приведет к утечке. Моя другая проблема с GC - это то, что она способствует ленивости в программистах. Я сторонник изучения концепций управления памятью на низком уровне, прежде чем перейти на более высокий уровень. Это похоже на математику. Вы узнаете, как решить корень квадрата, или как сначала взять производную от руки, затем вы узнаете, как это сделать на калькуляторе. Используйте эти вещи как инструменты, а не костыли.

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

Ответ 10

Моя точка зрения заключается в том, что GC уступает malloc при нормальном процедурном программировании. Вы просто переходите от процедуры к процедуре, распределяете и освобождаете, используете глобальные переменные и объявляете некоторые функции _inline или _register. Это стиль C.

Но как только вы выходите на более высокий уровень абстракции, вам нужно, по крайней мере, подсчет ссылок. Таким образом, вы можете передавать по ссылке, считать их и бесплатно, как только счетчик равен нулю. Это хорошо и превосходит malloc после того, как количество и иерархия объектов становятся слишком сложными для управления вручную. Это стиль С++. Вы определяете конструкторы и деструкторы для увеличения счетчиков, вы будете копировать-на-модификацию, поэтому разделяемый объект будет разделен на два, как только часть его будет изменена одной стороной, но другая сторона все еще нуждается в исходном значении. Таким образом, вы можете передавать огромное количество данных из функции в функцию, не думая, нужно ли вам копировать данные здесь или просто отправлять указатель там. Счет ref-count делает эти решения для вас.

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

Вы упомянули D, но D все еще улучшен С++, поэтому malloc или ref counting в конструкторах, распределения стека, глобальные переменные (даже если они являются составными деревьями сущностей всех видов), вероятно, вы выбираете.