Почему повторное использование массивов так сильно повышает производительность в C #? - PullRequest
9 голосов
/ 15 июня 2010

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

double[] tempDoubleArray = new double[M];

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

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

double[] tempDoubleArray = staticDoubleArray;

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

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

Ответы [ 3 ]

7 голосов
/ 15 июня 2010

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

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

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

1 голос
/ 15 июня 2010

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

См. Раздел, посвященный влиянию на производительность

  • существует стоимость выделения (прежде всего очистка выделенной памяти)
  • стоимость сбора (LOH и Gen2 собираются вместе - вызывая уплотнение крупных объектов в Gen2)
0 голосов
/ 15 июня 2010

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

...