Оптимизация объема памяти приложения .NET с огромным количеством экземпляров - PullRequest
1 голос
/ 17 июля 2010

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

public class MyObject
{
    public string Name;
    public object Tag;
    public DateTime DateTime1;
    public DateTime DateTime2;
    public DateTime DateTime3;
    public long Num1;
    public uint Num2;
    public uint Num3;
    public ushort Num4;
}

Во многих случаях я на самом деле не использую все поля или не использую весь размер поля.Так что я подумал, что, возможно, перенесу весь этот класс в интерфейс со свойствами и создадим множество реализующих классов, в которых данные хранятся по-разному: используются меньшие поля (например, int вместо long) и пропущены некоторые неиспользуемые поля.:

public interface IMyObject
{
    string Name { get; set; }
    object Tag { get; set; }
    DateTime DateTime1 { get; set; }
    DateTime DateTime2 { get; set; }
    DateTime DateTime3 { get; set; }
    long Num1 { get; set; }
    uint Num2 { get; set; }
    uint Num3 { get; set; }
    ushort Num4 { get; set; }
}

public class MyObject1 : IMyObject
{
    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1 { get; set; }
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1 { get; set; }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

public class MyObject2 : IMyObject
{
    private int _num1;

    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1 { get; set; }
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1
    {
        get { return _num1; }
        set { _num1 = (int)value; }
    }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

public class MyObject3 : IMyObject
{
    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1
    {
        get { return DateTime.MinValue; }
        set { throw new NotSupportedException(); }
    } 
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1 { get; set; }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

// ...

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

Еще одна мысль о строках:

Все строки в приложении .NET представлены в кодировке UTF-16.Если бы я только мог сделать это для кодирования в UTF-8, это уменьшило бы в 2 раза объем памяти, используемой строками.

Ответы [ 4 ]

2 голосов
/ 18 июля 2010

Мысль:

  • Есть ли какие-либо из общих строк? При загрузке данных вы можете использовать пользовательский интернер, чтобы гарантировать, что ни один из них не будет дублирован (примечание: не используйте встроенный встроенный , так как вы будете насыщать его;
  • существуют ли другие общие элементы, которые разумно могут быть дубликатами и могут быть перемещены в объекты? У вас все еще есть стоимость эталонного поля, но, надеюсь, это (и новый объект) будет чистой прибылью
  • Если у вас огромное количество похожих объектов, можно ли смоделировать какую-либо из них как неизменный тип-значение? Обычно это не мой предпочтительный вариант, но преимущество в том, что вы можете поместить их в массив:
    • вы получаете цену объекта-ссылки и объекта-заголовка для объекта
    • вы можете использовать смещение в массиве (int), а не ссылку; для 64-битных это приличная экономия при добавлении
  • вы, кажется, предлагаете разреженный объектный подход; и действительно, вы хотите избежать декартового произведения, но в то же время для небольшого числа членов, которое вы описываете , свойство-мешок, вероятно, будет больше дорого в памяти; Кроме того, поскольку вы упоминаете, что делаете это для повышения производительности, я подозреваю, что это тоже повредит процессору
  • DateTime с - они (например) всегда целые дни? Вы были бы удивлены тем, что вы можете получить, используя int количество дней в эпохе
1 голос
/ 17 июля 2010

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

Представляет ваш путь к файлу в виде Path, который представляет собой массив целых чисел, и FileName, который представляет собой строку и фактическое имя файла (это, вероятно, будет более уникальным, поэтому не стоит кодировать) Вы можете разбить путь на составные части, а затем использовать пару словарей для хранения прямого и обратного поиска. Таким образом, вы можете уменьшить путь к массиву целых. Гораздо меньше, чем строка.

1 голос
/ 17 июля 2010

Хранение строк в UTF8:

byte[] asciiStr = System.Text.Encoding.UTF8.GetBytes("asdf");

string text = System.Text.Encoding.UTF8.GetString(asciiStr);

(редактировать: сначала думал, что ASCII хотел)

Идея 1 : Если вы ожидаете, что большинство значений выиграло 'Чтобы заполнить большую часть времени, вы можете хранить каждое поле в отдельной структуре данных поиска значений ключей - словарь, упорядоченный список с двоичным поиском, двоичное дерево и т. д. Упорядоченный список с двоичным поискомвероятно, будет наиболее эффективным с точки зрения пространства, хотя поиск будет O (log n).

Так что вместо объектов MyObject [] у вас будет

Dictionary<int, string> names; // or List<Tuple<int,string>> names;
Dictionary<int, object> tags;
Dictionary<int, DateTime> datetime1s;
...

Где ключ int в каждомзначение - это идентификатор записи.

Идея 2 : Если вы уверены, что эти DateTimes находятся в разумно небольшом диапазоне (около 30 лет), скажем, с 1 января 2010 года, выможет преобразовать его в 32-битное значение типа int, представляющее, сколько секунд прошло с / до этой даты.Это сократит 4 байта на DateTime.

Идея 3 : Вы могли бы подумать о создании действительно компактной схемы сериализации, где первый байт каждого поля указывает, какое поле в классепоследующие байты сохраняются.Строковые значения могут быть просто разделены символом \ n или чем-то еще.Сохраните все это в байтовом массиве и десериализуйте его по требованию.

Так что-то вроде этого, без пробелов и в двоичных значениях, где это необходимо:

1 //indicates field 1 (Name)

beck.asf\n //the value

6 //indicates field 6 (Num1)

3545623 //the value, in a 64-bit binary int

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

0 голосов
/ 17 июля 2010

Как насчет использования System.Tuple? Вы можете динамически указать, какие поля вы хотите использовать.

редактирование:
Я бы определенно заглянул в String интернирование.

Также есть System.Dynamic.ExpandoObject

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