Пул массивов

Dec 15 2022
В настоящее время мы редко используем массивы. В большинстве случаев мы используем List<T>, который обеспечивает большую функциональность и основан на массиве под капотом.

В настоящее время мы редко используем массивы. В большинстве случаев мы используем List<T>, который обеспечивает большую функциональность и основан на массиве под капотом. Но бывают ситуации, когда производительность является ключевым фактором при работе со многими объектами.

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

В этой статье я покажу вам, как использовать ArrayPool, с примерами кода и оценкой производительности.

Репозиторий кода

Что такое ArrayPool

ArrayPool — это пул ресурсов, который позволяет повторно использовать экземпляры типов массивов. Вы можете использовать его для аренды и возврата буферов (массивов). Это улучшит производительность, когда вы часто создаете и уничтожаете массивы, которые занимают много памяти и GARBAGE COLLECTOR . Этот класс потокобезопасен, и вы можете использовать его одновременно. При возвращении в пул вы можете выбрать, очищать содержимое массива или нет. Очистка займет больше времени, но в вашей ситуации может потребоваться каждый раз получать пустой массив.

Как это использовать

Существует два способа доступа к массивам из памяти пула.

Первый и самый простой — использовать свойство ArrayPool Shared .

ArrayPool<T>.Общий

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

Более эффективный способ — инициализировать пул с помощью методов создания.

Пул массива<T>.Создать()

ArrayPool<T>.Create(int maxArrayLength, int maxArraysPerBucket)

Метод Create возвращает пользовательский экземпляр пула. Вы должны кэшировать его, чтобы получить к нему доступ из разных мест, чтобы повторно использовать его. Параметризованный метод может помочь достичь наилучшей производительности. Он создаст экземпляр, который группирует массивы длиной не более maxArrayLength в сегменты размером maxArraysPerBucket.

Воспользуйтесь преимуществами объединения массивов в C#, чтобы сократить выделение памяти и повысить производительность приложений.

Ориентир

Это простой тест. Сравним отличия между созданием нового массива и его арендой из пула. В каждом случае мы будем перебирать коллекцию из 1_000_000 элементов. Я также включу List<T> в тест, чтобы показать преимущества массивов в производительности.

Репозиторий кода

ориентир

Выводы

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

Существует заметная разница во времени между использованием стандартного массива и массива из пула. Разница во времени — это время, необходимое для выделения памяти при каждом создании нового массива. Конечно, есть и разница в распределении памяти. ArrayPool не нужно выделять новую память. Он повторно использует один и тот же блок.

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

В этом простом примере трудно показать разницу в производительности между общим и пользовательским ArrayPool. Это было бы заметно, когда используется много массивов разного размера.

Использование ArrayPool не всегда может быть самым удобным, но иногда оно может сэкономить вам много вычислительной мощности, когда это необходимо.