Почему многократное использование массивов увеличивает производительность так значительно в c#?

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

double[] tempDoubleArray = new double[M];

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

Профилирование показывает, что вызовы для построения массивов являются трудоемкими. Так, я решаю попытаться снова использовать массив путем создания этого статичным и многократного использования его. Это требует, чтобы некоторое дополнительное манипулирование выяснило минимальный размер массива, требуя дополнительной передачи через все задачи, но это работает. Теперь, программа намного быстрее (от 80 секунд до 22 секунд для выполнения всех задач).

double[] tempDoubleArray = staticDoubleArray;

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

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

9
задан Willem 15 June 2010 в 14:59
поделиться

3 ответа

Одним из ответов может быть куча больших объектов - объекты размером более 85 КБ размещаются в другом LOH, который реже собирается и не сжимается.

См. Раздел о влиянии на производительность

  • , есть стоимость выделения (в первую очередь очистка выделенной памяти)
  • стоимость сбора (LOH и Gen2 собираются вместе, вызывая уплотнение больших объектов в Gen2)
1
ответ дан 4 December 2019 в 22:27
поделиться

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

Помните, что создание массива - это не просто создание одной переменной, это создание N переменных (N - количество элементов в массиве). Повторное использование массивов - хороший способ увеличить производительность, хотя делать это нужно осторожно.

Чтобы пояснить, что я имею в виду под "созданием переменных" - это выделение места для них и выполнение всех действий, которые необходимо выполнить, чтобы сделать их пригодными для использования (т.е. инициализация значений нулем/null). Поскольку массивы являются ссылочными типами, они хранятся в куче, что немного усложняет жизнь, когда дело доходит до выделения памяти. В зависимости от размера массива (занимает ли он более 85 КБ общей памяти), он будет храниться либо в обычной куче, либо в куче больших объектов. Массив, хранящийся в обычной куче, как и все другие объекты кучи, может вызвать сборку мусора и уплотнение кучи (что включает в себя перемещение неиспользуемой в данный момент памяти, чтобы максимизировать смежное доступное пространство). Массив, хранящийся на Large Object Heap, не вызовет уплотнения (поскольку LOH никогда не уплотняется), но он может вызвать преждевременную сборку, заняв еще один большой непрерывный блок памяти.

7
ответ дан 4 December 2019 в 22:27
поделиться

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

0
ответ дан 4 December 2019 в 22:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: