Что касается производительности, каков будет ожидаемый относительный результат использования struct over class в этом списке? - PullRequest
0 голосов
/ 24 февраля 2012

Мы помещаем элементы журнала типа LogEntry в список типа List<LogEntry>, который будет сохранен в файл намного позже.

РЕДАКТИРОВАТЬ 1: Причина, по которой мы не сбрасываем журнал в файл сразу же, заключается в том, что оно находится в многопоточном приложении на Windows Phone с изолированным хранилищем. Запись в изолированное хранилище по своей сути медленная, за несколько миль от производительности настольных компьютеров. Затраты на очистку каждого сообщения немедленно фактически убивают параллельные переходы состояний и взаимодействия, которые мы хотим наблюдать с журналами. КОНЕЦ РЕДАКТИРОВАНИЯ 1

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

* 1013 Т.е. *

internal struct LogEntry
{
    public int ThreadId { get; private set; }
    public DateTime Timestamp { get; private set; }
    public string Message { get; private set; }

    public LogEntry(int threadId, DateTime timestamp, string message)
        : this()
    {
        this.ThreadId = threadId;
        this.Timestamp = timestamp;
        this.Message = message ?? string.Empty;
    }
}

или

internal sealed class LogEntry
{
    public int ThreadId { get; private set; }
    public DateTime Timestamp { get; private set; }
    public string Message { get; private set; }

    public LogEntry(int threadId, DateTime timestamp, string message)
    {
        this.ThreadId = threadId;
        this.Timestamp = timestamp;
        this.Message = message ?? string.Empty;
    }
}

Перед сохранением журнала в файл мы ничего не делаем для списка элементов, кроме добавления элементов. Мы не ищем, не удаляем и не перечисляем его - просто добавляем элементы.

Что скажете вы, struct или class действительно имеет значение?

РЕДАКТИРОВАНИЕ 2: Результат измерения времени добавления 60000 записей в потоке пользовательского интерфейса, чередующемся в очереди в рабочих элементах пула потоков, составил 65 секунд для случая struct и 69 секунд для class дело. Поскольку эта разница невелика, а реализация class немного чище и мне не нужна семантика равенства значений, я решил перейти на LogEntry как class. END EDIT 2

Ответы [ 4 ]

1 голос
/ 07 марта 2012

Результат моих измерений времени добавления 60000 записей в потоке пользовательского интерфейса, чередующемся в очереди в рабочих элементах пула потоков, составил 65 секунд для случая структуры и 69 секунд для случая класса.Поскольку эта разница невелика, а реализация класса немного чище, и мне не нужна семантика равенства значений по умолчанию, заданная struct, я решил использовать LogEntry как класс

1 голос
/ 24 февраля 2012

Если вы используете класс, каждое место хранения (переменная, поле структуры или элемент массива) этого типа будет 4/8 байтов (в x86 / x64), и каждый отчетливо созданный экземпляр этого типа, который по крайней мере, одна ссылка существует, займет 16/20 байтов для полей плюс дополнительные 8/16 байтов служебных данных, связанных с классом. Если вы используете структуру, каждое место хранения этого типа структуры будет занимать 16/20 байт, точка. Если вы не ожидаете, что многие экземпляры вашего типа будут иметь несколько мест хранения, указывающих на них, структура будет более эффективной. При использовании так называемых неизменяемых структур следует помнить, что утверждение:

  myLogEntry = new LogEntry(someId, someTime, someMessage)

будет фактически выполняться в C # как

  LogEntry temp;
  LogEntry.CallConstructor(ref temp, someId, someTime, someMessage); // not the real method name
  myLogEntry._ThreadId = temp._ThreadId;
  myLogEntry._Timestamp = temp._TimeStamp;
  myLogEntry._Message = temp._Message;

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

0 голосов
/ 24 февраля 2012

Краткий ответ: я бы пошел за класс.

Как тип значения, структура, когда создается в "локальной области видимости" (специфичной для одного класса или метода), обычно помещается в стек,Это обеспечивает быстрый доступ, и от него легко избавиться, когда он вам больше не нужен, так что это простой способ хранения простых локальных переменных.Однако в вашем случае этот тип значения станет элементом коллекции (это ссылочный тип, хранящийся в куче).Объекты в куче не могут ссылаться на объекты в стеке;объект должен быть скопирован из стека в кучу, и должна быть сделана ссылка на новый адрес кучи объекта.Этот процесс называется «боксом».Затем, когда структура присваивается другой локальной переменной или передается по значению в метод, она копируется обратно в место в стеке («распаковка»).Этот процесс упаковки и распаковки будет влиять на производительность, и его, как правило, следует избегать, где это возможно (конечно, бывают случаи, когда вы просто не можете его избежать; список типов значений не является плохим только потому, что он требует упаковки, еслиэто правильный инструмент для работы).

Класс, напротив, является «ссылочным типом», и при создании он по умолчанию сохраняется в куче.Это означает, что он может зависать столько времени, сколько необходимо (при условии, что он имеет хотя бы одну ссылку, сделанную на него другими переменными в области видимости), и все, что должно быть в стеке - это 32-разрядный целочисленный «указатель» (техническине то же самое, что указатель C / C ++, но похоже) на адрес объекта в куче.Чтобы получить доступ к элементам данных объекта, указатель должен быть «разыменован» для чтения необходимых данных из объекта в куче, но по большей части, пока класс не активно манипулируется, его гораздо легче обойти (единственный32-битная переменная вместо "широкого" многозначного чанка, копируемого в разных местах).

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

0 голосов
/ 24 февраля 2012

при запуске приложения печать памяти приложения в обоих случаях минимальна

если вы идете по пути стека (структуры),

  1. при запуске у вас будет экземпляр структуры, загруженный при запуске.
  2. со временем вы добавляете записи журнала в эту структуру
  3. - Печать памяти увеличивается
  4. вы фиксируете это в файле
  5. - вы очищаете содержимое структуры (я предполагаю)
  6. структура останется в стеке

если вы идете кучи (класс) путь

при запуске приложения создается на один экземпляр меньше при первом использовании класса, вы создаете экземпляр этого объекта вы добавляете записи журнала в этот класс - Печать памяти увеличивается (это будет с той же скоростью) вы фиксируете содержимое в файле и отменяете ссылку на этот объект и позволяете сборщику мусора выполнять свою работу

единственное и большое (- я считаю) различие заключается в сохранении ресурсов памяти во время жизни приложений

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

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