В С#/.NET делает динамический тип занимать меньше места, чем объект?

У меня есть консольное приложение, которое позволяет пользователям указывать переменные для обработки. Эти переменные входят в три варианта: строка, двойная и длинная (с двойными и длинными, безусловно, наиболее часто используемыми типами). Пользователь может указать любые переменные, которые им нравятся, и в любом порядке, чтобы моя система могла справиться с этим. С этой целью в моем приложении я хранили их как объект, а затем их литье/удаление из них по мере необходимости. например:

public class UnitResponse
{
    public object Value { get; set; }
}

Мое понимание заключалось в том, что бокс-объекты занимают немного больше памяти (около 12 байт), чем стандартный тип значения.

Мой вопрос: было бы более эффективно использовать ключевое слово dynamic для хранения этих значений? Это может обойти проблему бокса/распаковки, и если она более эффективна, как это повлияет на производительность?

ИЗМЕНИТЬ

Чтобы обеспечить некоторый контекст и предотвратить "вы уверены, что используете достаточное количество оперативной памяти, чтобы беспокоиться об этом", в моем худшем случае у меня 420 000 000 точек данных, чтобы беспокоиться (60 переменных * 7 000 000 записей). Это в дополнение к кучу других данных, которые я сохраняю по каждой переменной (включая несколько булевых и т.д.). Таким образом, уменьшение памяти имеет огромное влияние.

Ответ 1

ОК, поэтому реальный вопрос здесь: "У меня есть огромный набор данных, который я храню в памяти, как я могу оптимизировать его производительность как во времени, так и в памяти?"

Несколько мыслей:

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

Динамический не так; это бокс плюс целый ряд других накладных расходов. (Динамика С# очень быстро по сравнению с другими динамическими системами диспетчеризации, но она не является быстрой или малой в абсолютном выражении).

Это грубо, но вы могли бы рассмотреть возможность использования структуры, чей макет разделяет память между различными полями - например, объединение в C. Выполнение действительно действительно грубо и вовсе не безопасно, но может помочь в таких ситуациях. Сделайте поиск в Интернете для "StructLayoutAttribute"; вы найдете учебники.

  • Длинный, двойной или струнный, правда? Не может быть int, float или string? Действительно ли данные действительно превышают несколько миллиардов или точнее до 15 знаков после запятой? Разве int и float не выполняли эту работу в 99% случаев? Они в два раза меньше.

Обычно я не рекомендую использовать float over double, потому что это ложная экономика; люди часто экономят этот путь, когда у них ОДИН номер, например, экономия в четыре байта будет иметь значение. Разница между 42 миллионами поплавков и 42 миллионами удваивается.

  • Есть ли регулярность в данных, которые вы можете использовать? Например, предположим, что из ваших 42 миллионов записей есть только 100000 действительных значений, например, для каждого длинного, 100000 значений для каждого двойного и 100000 значений для каждой строки. В этом случае вы производите индексированное хранилище некоторого типа для длин, удвоений и строк, а затем каждая запись получает целое число, где младшие биты являются индексом, а два верхних бита указывают, какое хранилище нужно извлечь из него. Теперь у вас есть 42 миллиона записей, каждый из которых содержит int, а значения хранятся в какой-то красивой компактной форме где-то еще.

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

  • Помните, что память на самом деле является дисковым пространством; ОЗУ - это всего лишь удобный кэш. Если набор данных будет слишком большим для хранения в ОЗУ, тогда что-то собирается распечатать его на диск и прочитать его позже; это может быть вы или она может быть операционной системой. Возможно, вы знаете больше о своей локальности данных, чем в операционной системе. Вы можете записать свои данные на диск в удобной для просмотра форме (например, b-tree) и более эффективно хранить данные на диске и только приносить их в память, когда вам это нужно.

Ответ 2

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

И, конечно же, вы ничего не спасаете. "dynamic" - это просто "объект" с причудливой шляпой. "Динамические" объекты по-прежнему в коробке.

Ответ 3

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

Кроме того, все это усилие действительно стоит 12 байт на объект? Наверняка, там лучше использовать для вашего времени, чем сэкономить несколько килобайт (если это) ОЗУ? Вы доказали, что использование RAM вашей программой на самом деле является проблемой?

Ответ 4

Нет. Dynamic просто сохранит его как объект.

Скорее всего, это микро-оптимизация, которая не принесет никакой пользы. Если это действительно становится проблемой, тогда есть другие механизмы, которые вы можете использовать (generics) для ускорения работы.