2017-01-21 14 views
2

다음 두 가지 코드 중 어떤 경우가 더 나은지, 그 이유는 무엇입니까?GetOrAdded new to factory factory

.

private readonly ConcurrentDictionary<int, List<T>> _coll; 
_coll.GetOrAdd(1, new List<T>()); 

이 필요하지 않은 경우에도 모든 호출에 대한 새로운 List을 (우리가 capacity 0으로를 전달하는 경우이 사항이 여전히 얼마나 중요합니까?)을 작성합니다.

.

private readonly ConcurrentDictionary<int, List<T>> _coll; 
_coll.GetOrAdd(1, (val) => new List<T>()); 

이것은 단지 수요에 List을 만들지 만 위임 통화가 있습니다.

답변

4

메모리의 측면에서 매번 할당을 수행하는 첫 번째 방법이 있지만 변수를 캡처하지 않기 때문에 두 번째는 캐시 된 대리자 개체를 사용합니다. 컴파일러는 캐시 된 대리자의 생성을 처리합니다. List<T>의 기본 생성자는 초기화시 빈 배열을 사용하므로 0의 명시 적 용량과 동일하므로 0으로 설정된 첫 번째 경우와 차이가 없습니다.

실행 지침 측면에서 볼 때 동일한 경우 두 번째 인수가 사용되지 않았기 때문에 키가 발견됩니다. 키를 찾을 수없는 경우 첫 번째 방법은 로컬 변수를 읽는 반면 두 번째 방법은 델리게이트를 호출하는 간접 계층을 사용합니다. 또한 looking into the source code, 팩토리를 사용하는 GetOrAdd는 TryGetValue를 통해 추가 참조를 수행하여 팩토리 호출을 피하는 것으로 나타납니다. 위임자는 잠재적으로 여러 번 실행될 수도 있습니다. GetOrAdd는 단순히 팩토리가 한 번만 호출되는 것이 아니라 사전에 하나의 항목이 있음을 보장합니다.

요약하면 어쨌든 할당이 필요하고 대리인을 통한 간접 참조가 없기 때문에 일반적으로 키를 찾을 수없는 경우 첫 번째 방법이 더 효과적 일 수 있습니다. 그러나 키가 일반적으로 발견되면 할당 수가 적기 때문에 두 번째 방법이 더 효과적입니다. 캐시에서의 구현을 위해서는 일반적으로 히트 수가 많을 것으로 예상되므로, 이것이 어디에 해당되는지는 두 번째 방법을 권장합니다. 실제로이 둘의 차이점은이 코드 경로에서 전체 응용 프로그램의 할당이 얼마나 중요한지에 달려 있습니다.

또한 이것을 사용하는 모든 구현은 스레드 안전성이 보장되지 않으므로 반환되는 List<T> 주변의 잠금을 구현해야 할 수 있습니다.

+0

예를 들어'List '을 사용 했으므로 실제 컬렉션은 스레드로부터 안전합니다. 또한 Joseph의 제안을 참고하면'() => Enumerable.Empty ()'이 성능 향상에 도움이됩니까? – Hele

+1

또한 변경 사항이 바람직하지 않은 부작용이있는 .NET 4.5의 경우처럼 어떤 프레임 워크 버전이 사용되고 있는지 (새로운 동작이 구현되었는지 여부에 따라) 나타납니다. https://basildoncoder.com/blog/concurrentdictionary-getoradd-vs.html – Alex

+0

@Hele'Enumerable.Empty ()'을 사용한다면 델리게이트는 필요 없습니다.'Enumerable.Empty ()'는 정적 읽기 전용 필드를 읽습니다. 그러나 왜 IEnumerable '과 같은 읽기 전용 인터페이스를 사용해야하는지 잘 모르겠습니다. 나는 그것이 업데이 트를 어렵게 만들 것이라고 생각하지만, 코드를 더 볼 필요가있다. –

0

매우 큰 데이터 세트로 작업하지 않는 한 성능면에서 차이가 많이 나는 것을 상상할 수 없습니다. 또한 각 아이템에 얼마나 많은 피해가 있는지에 달려 있습니다. 제네릭은 런타임 수준에서 매우 잘 최적화되어 있으며 대리자를 사용하면 할당이 어느쪽으로 든 이루어집니다.

내 제안은 Enumerable.Empty<T>()을 사용하는 것이므로 각 배열 항목에 대한 할당량을 절약 할 수 있습니다.