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

Как эти люди избегают создания мусора?

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

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

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

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

4b9b3361

Ответ 1

Я нашел статью, с которой вы связались, с недостатком:

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

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

Ответ 2

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

Еще одна вещь, которую стоит рассмотреть здесь, заключается в том, что они по существу не так сильно избавились от 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. В С++ операторы new и delete могут быть переопределены, что позволяет изменять поведение и поведение по умолчанию для изменения, и обсуждение того, как и почему это может сделать, может вас заинтересовать.

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

Ответ 3

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

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

Ответ 4

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

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

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

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

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

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

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

Ответ 5

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

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

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

Удачи.