С GC или не ГК


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

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

этот, Андрей Александреску, мотивирует a переход от C / C++ к его новой игре-чейнджер D. Большинство вещей D кажется действительно хорошо мотивированным и разработанным. Одна вещь, однако, удивила меня, а именно, что D толкает для сборки мусора и что все классы создаются исключительно по ссылке. Еще больше сбивает с толку, книга Руководство По Использованию Языка Программирования D в частности, в разделе о Управление Ресурсами указано следующее, цитата:

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

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

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

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

10 70

10 ответов:

чтобы напрямую ответить на два вопроса:

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

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

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

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

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

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

ответ 1):

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

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

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

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

ответ 2):

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

Так что если

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

вы можете в конечном итоге с более высокой производительностью.

ответ на незаданный вопрос:

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

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

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

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

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

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

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 сжимает ваши объекты кучи вместе. Он также борется с фрагментацией кучи, как объясняется в ответе Джека Эдмондса. Подобные вещи можно сделать с ручным управлением памятью, но это дополнительная работа.

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

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

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

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

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

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

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