Как эти люди избегают создания мусора? - PullRequest
16 голосов
/ 07 августа 2010

Вот интересная статья , которую я нашел в Интернете.

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

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

Кроме того, мне нужен специальный инструмент для просмотра памяти? До сих пор я использовал профилировщик памяти SciTech.

Ответы [ 5 ]

9 голосов
/ 07 августа 2010

Я обнаружил, что бумага, на которую вы ссылались, довольно неполноценна:

  • Она предполагает и хочет, чтобы вы предположили, что сборщик мусора является основной причиной задержки.Они не объяснили, почему они так думают, и не объяснили, каким образом их система не является в основном замаскированным сборщиком мусора.
  • В нем говорится о количестве памяти очищается в сборщике мусора, что не имеет значения: время, затрачиваемое на сборку мусора, больше зависит от количества объектов , независимо от их размера.
  • Таблица «Результаты »внизу не дает никакого сравнения с системой, которая использует сборщик мусора .NET.

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

5 голосов
/ 07 августа 2010

С самого начала следует отметить, что, по их словам, «общепринятым мнением является разработка технологии обмена сообщениями с малой задержкой, требующей использования неуправляемого C ++ или языка ассемблера».В частности, они говорят о некоем случае, когда люди часто отвергают решение .NET (или Java) из-под контроля.В этом отношении, относительно наивное решение C ++, вероятно, также не получило бы оценку.

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

Есть несколько различных способов сделать это вместо этого.Вот одинСкажем, мне нужно создать и уничтожить несколько объектов Foo во время работы моего приложения.Создание Foo параметрируется с помощью int, поэтому обычный код будет выглядеть следующим образом:

public class Foo
{
    private readonly int _bar;
    Foo(int bar)
    {
        _bar = bar;
    }
    /* other code that makes this class actually interesting. */
}

public class UsesFoo
{
    public void FooUsedHere(int param)
    {
        Foo baz = new Foo(param)
        //Do something here
        //baz falls out of scope and is liable to GC colleciton
    }
}

Весьма другой подход:

public class Foo
{
    private static readonly Foo[] FOO_STORE = new Foo[MOST_POSSIBLY_NEEDED];
    private static Foo FREE;
    static Foo()
    {
        Foo last = FOO_STORE[MOST_POSSIBLY_NEEDED -1] = new Foo();
        int idx = MOST_POSSIBLY_NEEDED - 1;
        while(idx != 0)
        {
            Foo newFoo = FOO_STORE[--idx] = new Foo();
            newFoo._next = FOO_STORE[idx + 1];
        }
        FREE = last._next = FOO_STORE[0];
    }
    private Foo _next;
    //Note _bar is no longer readonly. We lose the advantages
    //as a cost of reusing objects. Even if Foo acts immutable
    //it isn't really.
    private int _bar;
    public static Foo GetFoo(int bar)
    {
        Foo ret = FREE;
        FREE = ret._next;
        return ret;
    }
    public void Release()
    {
        _next = FREE;
        FREE = this;
    }
    /* other code that makes this class actually interesting. */
}

public class UsesFoo
{
    public void FooUsedHere(int param)
    {
        Foo baz = Foo.GetFoo(param)
        //Do something here
        baz.Release();
    }
}

Дополнительные сложности могут быть добавлены, если вы многопоточны (хотядля действительно высокой производительности в неинтерактивной среде вам может потребоваться один поток или отдельные хранилища классов Foo для каждого потока), и если вы не можете заранее предсказать MOST_POSSIBLY_NEEDED (самое простое - создать новый Foo () по мере необходимости, но не выпускайте их для GC, что можно легко сделать в приведенном выше коде, создав новый Foo, если FREE._next имеет значение null).

Если мы разрешаем небезопасный код, мы можем получить еще большие преимущества, имея Fooструктура (и, следовательно, массив, содержащий непрерывную область памяти стека), _next - указатель на Foo, и GetFoo (), возвращающий указатель.

Является ли это тем, чем на самом деле занимаются эти люди, я, конечно,не могу сказать, но вышеизложенное не позволяет активировать GC.Это будет быстрее только в условиях очень высокой пропускной способности, а если нет, то позволить GC делать свое дело, вероятно, лучше (GC действительно помогает вам, несмотря на то, что 90% вопросов о том, что он воспринимает это как «большой плохой»).Есть и другие подходы, которые так же избегают GC.В C ++ операторы new и delete могут быть переопределены, что позволяет изменить поведение создания и уничтожения по умолчанию, и обсуждения того, как и почему это можно сделать, могут вас заинтересовать.

Практический вывод из этогоэто когда объекты либо содержат ресурсы, кроме памяти, которые дороги (например, соединения с базами данных), либо «изучают», как они продолжают использоваться (например, XmlNameTables).В этом случае пул объектов полезен (соединения ADO.NET делают это за кулисами по умолчанию).В этом случае, хотя простой путь - путь, поскольку дополнительные издержки с точки зрения памяти не имеют значения.Вы также можете отказаться от объектов при конфликте блокировок (вы стремитесь повысить производительность, и конфликт блокировок нанесет им больший вред, чем при отказе от объекта), что, я сомневаюсь, сработает в их случае.

2 голосов
/ 07 августа 2010

Я некоторое время работал с продуктом CEP под названием StreamBase . Один из их инженеров сказал мне, что они переносят свой код C ++ на Java, потому что они получают лучшую производительность, меньше ошибок и лучшую переносимость в JVM, почти полностью избегая GC. Я полагаю, что аргументы применимы и к CLR.

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

Вот некоторая информация с их сайта :

StreamBase избегает сборки мусора двумя способами: не использует объекты и использует только минимальный набор необходимых нам объектов.

Во-первых, мы избегаем использования объектов, используя примитивные типы Java (Boolean, byte, int, double и long) для представления наших данных для обработки. Каждый тип данных StreamBase представлен одним или несколькими типами примитивов. Управляя только примитивными типами, мы можем эффективно хранить данные в выделенных областях стека или массива. Затем мы можем использовать методы, такие как параллельные массивы или вызовы методов, для эффективной передачи данных.

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

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

2 голосов
/ 07 августа 2010

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

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

0 голосов
/ 07 августа 2010

В 99% случаев вы будете тратить деньги боссов, пытаясь достичь этого.В статье описывается абсолютно экстремальный сценарий, когда им нужно последнее падение производительности.Как вы можете прочитать в этой статье, существуют отличные части .NET Framework, которые нельзя использовать, когда вы пытаетесь быть без GC.Некоторые из самых основных частей BCL используют выделение памяти (или «производят мусор», как это называется в статье).Вам нужно будет найти способ обойти эти методы.И даже когда вам нужны абсолютно быстрые приложения, вам лучше сначала попытаться создать приложение / архитектуру, которая может масштабироваться (использовать несколько компьютеров), прежде чем идти по маршруту без GC.Единственная причина, по которой они используют маршрут без GC, заключается в том, что им нужна абсолютная низкая задержка.IMO, когда вам нужна абсолютная скорость, но вам не нужно абсолютное минимальное время отклика, будет сложно обосновать архитектуру без GC.Кроме того, если вы попытаетесь создать клиентское приложение без GC (например, Windows Forms или WPF App);Забудьте об этом, эти структуры представления постоянно создают новые объекты.

Но если вы действительно этого хотите, это на самом деле довольно просто.Вот простой способ:

  • Выяснить, какие части .NET API нельзя использовать (вы можете написать инструмент, который анализирует сборки .NET с использованием механизма самоанализа ).
  • Напишите программу, которая проверяет код, который вы или ваши разработчики пишете, чтобы убедиться, что они не размещают напрямую или не используют «запрещенные» методы .NET, используя список безопасности, созданный в предыдущем пункте (FxCopотличный инструмент для этого).
  • Создание пулов объектов, которые вы инициализируете во время запуска.Остальная часть программы может повторно использовать существующий объект, чтобы им не приходилось выполнять new ops.
  • Если вам нужно манипулировать строками, используйте для этого байтовые массивы и сохраняйте байтовые массивы в пуле (WCF также использует эту технику).Вам нужно будет создать API, который позволит манипулировать этими байтовыми массивами.
  • И, наконец, профиль, профиль, профиль.

Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...