간략한 소개
성능 최적화는 일반적으로 CPU나 메모리, 그리고 운영체제 IO 핸들, 네트워크 트래픽, 디스크 사용량 등 더 적은 자원으로 동일한 수의 요청을 처리하도록 보장하는 방법입니다. 하지만 대부분의 경우, 우리는 CPU와 메모리 사용량을 줄이고 있습니다. 앞서 공유한 콘텐츠에는 몇 가지 제한이 있어서 직접 변환하기 어렵습니다. 오늘은 몇 가지 컬렉션 유형만 교체하면 성능이 향상되고 메모리 사용량을 줄이는 간단한 방법을 공유하고자 합니다. 오늘은 여러분과 함께 수업용 도서관을 공유하고자 합니다.클래스 라이브러리는 Collections.Pooled라고 불립니다이름에서 알 수 있듯이, 이 기능은 메모리 사용량과 GC를 줄이기 위해 풀링 메모리를 통해 이루어졌으며, 성능 향상과 소스 코드도 직접 확인할 예정입니다.
컬렉션. 풀
프로젝트 링크:하이퍼링크 로그인이 보입니다.
이 라이브러리는 System.Collections.Generic의 클래스를 기반으로 하며, 새로운 System.Span <T>및 System.Buffers.ArrayPool 클래스 라이브러리를 활용해 <T>메모리 할당을 줄이고 성능을 개선하며 최신 API와의 상호운용성을 높이기 위해 수정되었습니다. Collections.Pooled는 .NET Standnd 2.0(.NET Framework 4.6.1+)과 . NET Core 2.1+. 광범위한 단위 테스트와 벤치마크 세트가 CoreFX에서 이식되었습니다.
총 검사 수: 27,501회. 출처: 27501. 실패: 0. 건너뛰기: 0. 시험 운전은 성공적이었습니다. 테스트 실행 시간: 9.9019초
사용 방법
이 라이브러리는 Nuget, NuGet Version을 통해 쉽게 설치할 수 있습니다.
Collections.Pooled 라이브러리에서는 .NET 네이티브 타입과의 비교에서 볼 수 있듯이, 우리가 일반적으로 사용하는 컬렉션 타입에 대한 풀 버전을 구현하고 있습니다.
| . .NET 네이티브 | 컬렉션. 풀 | 말 | | 목록<T> | 풀드리스트<T> | 일반 컬렉션 클래스 | | Dictionary<TKey, TValue> | PooledDictionary<TKey, TValue> | 일반 사전 클래스 | | 해시셋<T> | 풀드셋<T> | 일반 해시 컬렉션 클래스 | | 스택<T> | 스택<T> | 일반 스택 | | 큐<T> | 풀드큐<T> | 일반 코호트 |
사용할 때는 해당 의 . 아래 코드에 나와 같이 Collections.Pooled 버전을 사용하는 .NET 네이티브 버전:
하지만 Pooled 타입은 Dispose() 메서드를 통해 사용한 메모리를 풀로 반환하는 IDispose 인터페이스를 구현하고 있으므로, Pooled 컬렉션 객체를 사용한 후에는 Dispose() 메서드를 호출해야 합니다. 또는 using var 키워드를 직접 사용할 수도 있습니다.
참고: Collections.Pooled 내의 컬렉션 객체를 사용하세요수동으로 풀어주는 것이 가장 좋습니다하지만 해제하지 않아도 결국 GC가 재활용하긴 하지만, 풀로 되돌릴 수 없고 메모리 절약 효과도 달성하지 못합니다. 메모리 공간을 재사용하기 때문에 풀에 메모리 공간을 반환할 때 컬렉션 내 요소를 처리해야 하며, 다음과 같이 정의된 ClearMode라는 열거 기능을 제공합니다:
기본적으로는 기본값인 Auto를 사용할 수 있고, 특별한 성능 요구가 있다면 위험을 알게 된 후에는 Never를 사용할 수 있습니다. 참조 타입과 참조 타입을 포함하는 값 타입의 경우, 메모리 공간을 풀로 반환할 때 배열 참조를 비워야 합니다. 지우지 않으면 GC는 이 메모리 공간을 해제할 수 없습니다(요소의 참조가 항상 풀에 의해 저장되어 있었기 때문입니다). 순수 값 타입이라면 비울 수 없습니다. 이 글에서는 참조 타입과 struct(값 타입) 배열의 저장 차이를 설명합니다. 순수 값 타입은 객체 헤더 재활용이 없으며 GC의 개입이 필요하지 않습니다.
. .NET 성능 최적화 - struct 대체 클래스 사용:하이퍼링크 로그인이 보입니다.
성능 비교
저는 벤치마크만 하지 않았고, 직접 사용한 오픈 소스 프로젝트의 실행 점수는 많은 프로젝트의 메모리 사용량이 0이었는데, 이는 통합 메모리 할당이 없었기 때문입니다.
풀드리스트<T>
벤치마크에 추가된 2048개의 원소를 순환 분석해 보세요. .NET 네이티브 리스트는 <T>110us(실제 벤치마크 결과에 따르면 수치의 밀리초 단위는 사무적 오류)와 263KB 메모리를 필요로 하는 반면, PooledList는 <T>36us와 0KB 메모리만 필요합니다.
PooledDictionary<TKey, TValue>
벤치마크에서 반복 회로 10_0000개의 요소를 사전에 추가합니다. .NET 네이티브 Dictionary<TKey, TValue>는 11ms와 13MB의 메모리가 필요하지만, PooledDictionary<TKey, TValue>는 7ms와 0MB의 메모리만 필요합니다.
풀드셋<T>
벤치마크에서 해시 컬렉션을 루프하여 10_0000개의 요소를 더합니다. .NET 네이티브 HashSet은 <T>5348ms와 2MB가 필요한 반면, PooledSet은 <T>4723ms와 0MB 메모리만 필요합니다.
풀드스택<T>
벤치마크에서 스택을 루프하여 10000개의 요소를 추가합니다. .NET 네이티브 PooledStack은 <T>1079ms와 2MB가 필요한 반면, PooledStack은 <T>633ms와 0MB 메모리만 필요합니다.
풀드큐<T>
벤치마크에서 루프를 루프하여 큐에 10_0000개의 요소를 추가합니다. .NET 네이티브 <T>PooledQueue는 681ms와 1MB가 필요하며, PooledQueue는 <T>408ms와 0MB의 메모리만 필요합니다.
장면은 수동으로 해제되지 않습니다
또한 앞서 언급했듯이, 풀된 컬렉션 유형은 반드시 공개되어야 하지만, 출시되지 않아도 GC는 재활용하기 때문에 상관없습니다.
벤치마크 결과는 다음과 같습니다:
결론은 위의 벤치마크 결과에서 도출할 수 있습니다.
Pooled 타입 컬렉션을 제때 해제하면 GC가 거의 트리거되지 않고 메모리를 할당하며, 위 그래프에서 56바이트의 메모리만 할당됩니다. 풀드 타입 컬렉션이 해제되지 않더라도, 풀에서 메모리를 할당하기 때문에 ReSize 확장 작업 중 메모리를 재사용하고, 비교적 빠른 GC 할당 메모리 초기화 단계를 건너뛸 수 있습니다. 가장 느린 방법은 일반적인 컬렉션 타입을 사용하는 것으로, 각 ReSize 확장 연산은 새로운 메모리 공간에 적용되어야 하며, GC는 이전 메모리 공간도 다시 확보해야 합니다.
원리 분석
제 이전 블로그 글을 읽으셨다면, 컬렉션 타입의 초기 크기를 설정하고 C# 사전의 구현 원리를 분석해야 합니다. .NET BCL 개발자들이 이 기본 컬렉션 타입의 기본 데이터 구조를 고성능 랜덤 액세스를 위한 배열로 사용한다는 것을 알 수 있습니다. 예를 들어 List를 예로 들어 보겠습니다<T>.
추가된 요소를 저장할 새 배열을 만드세요. 배열에 공간이 충분하지 않으면, 확장 연산이 트리거되어 공간 크기를 두 배로 요청합니다. 구성자 코드는 다음과 같으며, 직접 생성된 제네릭 배열임을 알 수 있습니다:
그래서 메모리를 풀하고 싶다면, 클래스 라이브러리에서 새 키워드 애플리케이션이 사용되는 위치를 풀드 애플리케이션으로 바꾸기만 하면 됩니다. 여기서 여러분과 나누고 있습니다. NET BCL은 ArrayPool이라는 유형으로, 재사용 가능한 일반 인스턴스로 구성된 배열 자원 풀을 제공하여 GC에 가해지는 부담을 줄이고 자주 배열을 생성 및 폐기할 경우 성능을 향상시킬 수 있습니다.
Pooled 타입의 기본 계층은 ArrayPool을 사용해 자원 풀을 공유하는 것이고, 그 구성자에서 기본적으로 ArrayPool을 사용하는 것을 알 수 있습니다<T>. 배열 객체를 할당하기 위해 공유하고, 물론 직접 ArrayPool을 만들어 사용할 수도 있습니다.
또한, 용량 조정 작업(확장)을 수행할 때는 기존 배열이 스레드 풀로 반환되고, 새 배열도 풀에서 획득됩니다.
또한, 저자는 Span을 사용해 Add 및 Insert와 같은 API를 최적화하여 더 나은 랜덤 액세스 성능을 제공합니다. 또한 TryXXX 시리즈 API가 추가되어 더 편리한 방식으로 사용할 수 있습니다. 예를 들어, List 클래스는 <T><T>PooledList에 비해 최대 170개의 수정 사항이 있습니다.
요약
실제 온라인 사용에서는 네이티브 컬렉션 유형을 Pooled에서 제공하는 컬렉션 형식으로 대체할 수 있는데, 이는 메모리 사용량과 P95 지연 시간을 줄이는 데 매우 유용합니다. 또한, 출시를 잊어도 네이티브 컬렉션 타입을 사용하는 것보다 성능이 크게 나빠지지 않을 것입니다. 물론, 가장 좋은 습관은 제때 해방하는 것입니다.
원문 언어:하이퍼링크 로그인이 보입니다.
|