Короткий вступ
Оптимізація продуктивності — це те, як забезпечити обробку однакової кількості запитів з меншою кількістю ресурсів, які зазвичай є процесором або пам'яттю, а також, звісно, обробками операційної системи, мережевим трафіком, використанням диска тощо. Але здебільшого ми зменшуємо використання процесора та пам'яті. Контент, яким вже ділилися раніше, має певні обмеження, його важко трансформувати напряму, сьогодні я хочу поділитися з вами простим методом — потрібно лише замінити кілька типів колекцій, щоб досягти ефекту покращення продуктивності та зменшення обсягу пам'яті. Сьогодні я хочу поділитися з вами класною бібліотекою, ось цієюБібліотека класу називається Collections.Pooled, Як видно з назви, це здійснюється через об'єднану пам'ять, щоб досягти мети зменшення обсягу пам'яті та GC, і ми безпосередньо побачимо її продуктивність, а також познайомимося з вихідним кодом, чому він приносить ці покращення продуктивності.
Колекції. Об'єднані
Посилання на проєкт:Вхід за гіперпосиланням видно.
Бібліотека базується на класах у System.Collections.Generic, які були модифіковані для використання нових <T>бібліотек класів System.Span та System.Buffers.ArrayPool <T>з метою зменшення виділення пам'яті, підвищення продуктивності та забезпечення більшої сумісності з сучасними API. Collections.Pooled підтримує .NET Standnd 2.0 (.NET Framework 4.6.1+), а також підтримку . NET Core 2.1+. Великий набір юніт-тестів і бенчмарків було портовано з CoreFX.
Загальна кількість тестів: 27501. Віко: 27501. Невдачі: 0. Пропустити: 0. Тестовий запуск пройшов успішно. Час виконання тесту: 9,9019 секунди
Як користуватися
Цю бібліотеку можна легко встановити через Nuget, версію NuGet.
У бібліотеці Collections.Pooled вона реалізує пулні версії для типових типів колекцій, які ми зазвичай використовуємо, як показано у порівнянні з нативними типами .NET.
| . .NET native | Колекції. Об'єднані | Зауваження | | Список<T> | PooledList<T> | Класи генеричних колекцій | | Словник<TKey, TValue> | PooledDictionary<TKey, TValue> | Клас універсального словника | | HashSet<T> | PooledSet<T> | Універсальні класи колекції хешів | | Стек<T> | Стек<T> | Генеричні стеки | | Черга<T> | PooledQueue<T> | Генерична когорта |
При використанні потрібно додати відповідне . Нативна версія .NET з версією Collections.Pooled, як показано в коді нижче:
Однак слід зазначити, що тип Pooled реалізує інтерфейс IDispose, який повертає використану пам'ять у пул через метод Dispose(), тому потрібно викликати його метод Dispose() після використання об'єкта колекції Pooled. Або можна використовувати ключове слово з використанням var безпосередньо.
Примітка: Використовуйте об'єкт колекції всередині Collections.PooledКраще звільняти його вручну, але це не має значення, якщо ви його не випустите, GC зрештою його перероблять, але його не можна повернути в пул, і це не досягне ефекту збереження пам'яті. Оскільки він повторно використовує простір пам'яті, при поверненні пам'яті до пулу потрібно обробити елементи колекції, і він надає перелік під назвою ClearMode для використання, визначений так:
За замовчуванням ви можете використовувати стандартне значення Auto, а якщо є особливі вимоги до продуктивності — Never після знати ризики. Для еталонних типів і значень, що містять типи посилання, ми повинні спорожнити посилання на масив при поверненні простору пам'яті до пулу; якщо це не очищено, GC не зможе звільнити цю частину пам'яті (оскільки посилання елемента завжди утримувалося пулом), якщо це чистий тип значення, то його не можна спорожнити. У цій статті я описую різницю в пам'яті між типами посилання та масивами struct (value type), чисті типи значень не мають рециклування заголовка об'єкта і не потребують втручання GC.
. .NET оптимізація продуктивності — використовуйте альтернативні класи структур:Вхід за гіперпосиланням видно.
Порівняння продуктивності
Я не робив Benchmark самостійно, і результати відкритих проектів, які я безпосередньо використовував, були нулевими для використання пам'яті багатьох проєктів, що було через те, що використана спільна пам'ять не мала додаткового виділення.
PooledList<T>
Пройдіть по 2048 елементах, доданих до набору в Benchmark, . .NET Native <T>List вимагає 110us (згідно з фактичними результатами бенчмарку, мілісекунди на рисунку мають бути помилкою) і 263 КБ пам'яті, тоді як <T>PooledList потребує лише 36 КБ і 0 КБ пам'яті.
PooledDictionary<TKey, TValue>
Додайте 10_0000 елементів до словника у циклі в бенчмаркі, . .NET нативний словник<TKey, TValue> потребує 11 мс і 13 МБ пам'яті, тоді як PooledDictionary<TKey, TValue> потребує лише 7 мс і 0 МБ пам'яті.
PooledSet<T>
Пройшовши через колекцію хешів у Benchmark, додайте 10_0000 елементів, . Нативний .NET HashSet <T>потребує 5348мс і 2 МБ, тоді як <T>PooledSet — лише 4723мс і 0 МБ пам'яті.
PooledStack<T>
Пройдіть стек у Benchmark, щоб додати 10_0000 елементів, . Нативний .NET <T>PooledStack потребує 1079мс і 2 МБ, тоді як PooledStack <T>потребує лише 633мс і 0 МБ пам'яті.
PooledQueue<T>
Пройдіть цикли в бенчмарку, щоб додати 10_0000 елементів до черги, . Рідний .<T>NET PooledQueue потребує 681мс і 1 МБ, тоді як <T>PooledQueue потребує лише 408мс і 0 МБ пам'яті.
Сцена не звільняється вручну
Крім того, ми згадували вище, що тип спільної колекції потрібно випустити, але не має значення, якщо він не буде випущений, бо GC буде переробляти.
Результати Benchmark такі:
Висновок можна зробити з наведених вище результатів бенчмарку.
Випуск колекції типів Pooled вчасно майже не запускає GC і виділяє пам'ять, з наведеного вище графіка виділяється лише 56 байт пам'яті. Навіть якщо колекція типів Pooled не буде опублікована, оскільки вона виділяє пам'ять із пулу, вона все одно повторно використовуватиме пам'ять під час операції розширення ReSize і пропускатиме етап ініціалізації пам'яті виділення GC, який є відносно швидким. Найповільніший варіант — використовувати звичайний тип колекції, кожна операція розширення ReSize має застосовуватися до нового простору пам'яті, а також GC має повернути попередній простір пам'яті.
Аналіз принципів
Якщо ви читали мій попередній допис у блозі, вам слід встановити початковий розмір для типів колекцій і проаналізувати принцип реалізації C# Dictionary, ви можете знати, що розробники .NET BCL використовують базові структури даних цих базових типів колекцій як масиви для високопродуктивного випадкового доступу, візьмемо <T>List як приклад.
Створіть новий масив для зберігання доданих елементів. Якщо в масиві недостатньо місця, запускається операція розгортання для запиту подвоєного розміру простору. Код конструктора виглядає так, і ви можете побачити, що це загальний масив, створений безпосередньо:
Отже, якщо ви хочете об'єднати пам'ять, потрібно лише змінити місце, де використовується нова додатка ключового слова в бібліотеці класів, щоб використовувати пулований додаток. Ось я ділюся цим з вами. NET BCL — це тип під назвою ArrayPool, який надає масивний ресурсний пул багаторазових універсальних екземплярів, що можна використовувати для зменшення навантаження на GC і підвищення продуктивності у разі частого створення та руйнування масивів.
Базовий шар нашого типу Pooled полягає у використанні ArrayPool для спільного використання пулів ресурсів, і з його конструктора видно, що він використовує ArrayPool за замовчуванням<T>. Спільний для призначення масивних об'єктів, і, звичайно, ви також можете створити власний ArrayPool для його використання.
Крім того, при виконанні операції коригування ємності (розширення) старий масив повертається до пулу потоків, а новий масив також отримується з пулу.
Крім того, автор використовує Span для оптимізації API, таких як Add і Insert, щоб забезпечити їм кращу продуктивність у довільному доступі. Крім того, додано API серії TryXXX, тож ви можете використовувати його зручніше. Наприклад, <T>клас List <T>має до 170 модифікацій порівняно з PooledList.
зведення
У нашому реальному онлайн-використанні ми можемо замінити нативний тип колекції на тип колекції, наданий Pooled, що дуже допомагає зменшити використання пам'яті та затримку P95. Також, навіть якщо забудете його випустити, продуктивність не буде набагато гіршою, ніж при використанні рідного типу колекції. Звісно, найкраща звичка — випускати його вчасно.
Оригінальний:Вхід за гіперпосиланням видно.
|