오 CollectionsMarshal.AsSpan()
을 이용하면 좀 더 빠르게 열거할 수 있군요. 물론… 열거 중에 수정될 수 있으므로 조심스럽게 사용해야겠습니다.
https://www.meziantou.net/fastest-way-to-enumerate-a-list-t.htm
오 CollectionsMarshal.AsSpan()
을 이용하면 좀 더 빠르게 열거할 수 있군요. 물론… 열거 중에 수정될 수 있으므로 조심스럽게 사용해야겠습니다.
https://www.meziantou.net/fastest-way-to-enumerate-a-list-t.htm
아니 이런 유익한 정보가…
오오! 굉장하군요.
개인적으로 궁굼해서 테스트를 따라해봤는데요.
디버그일 때에는 놀랍게도 List_ForEach 가 가장 빠르더라구요.
(하지만 릴리즈에서는 가장 느렸…;ㅅ;)
근데 좀 아쉬운 건 AsSpan() 의 인자가 List<T>?
인 게 좀 아쉽습니다.
LINQ 를 이용한 IQuerable 타입에서는 혜택을 보기가 어렵지 않을까 싶네요.
(아님 제가 모르는 방법이 따로 있을까요?ㅅ?)
오늘 @Vincent 님 덕분에 List<T>
구현을 살펴볼 수 있었는데, CollectionsMarshal.AsSpan()
이 되려 List<T>
를 처리할 수 있었던 이유는 List<T>
의 구현이 연속된 배열
이기 때문입니다.
https://forum.dotnetdev.kr/t/c-list-add/2349
CollectionsMarshal.AsSpan()
의 구현을 보면 단순히
List<T>
의 내부 연속 배열을 Span<T>
로 변경했음을 알 수 있습니다.
public static Span<T> AsSpan<T>(List<T>? list)
=> list is null ? default : new Span<T>(list._items, 0, list._size);
그러니 연속된 메모리만
Span<T>
의 후보가 된다는 점은 변함이 없네요. ^^;
CollectionsMarshal
에는 유용한 GetValueRefOrNullRef()
와 GetValueRefOrAddDefault()
메소드도 있습니다.
C#에서는 사전 키로 값을 설정하거나 변경하고자 할 때 dictionary[key] = value
형태로 사용해야만 합니다. 이 작업은 의외로 몇 단계의 처리가 필요한데, 먼저 키로 탐색을 하고 찾으면 값을 변경하고 없으면 키에 값을 설정합니다.
그런데 다음처럼 동작해야 한다고 칩시다. 찾고자 하는 단어의 개수를 사전에 저장해야 하는데 대상이 100만건이고 그 결과를 반드시 사전의 특정 키 값으로 저장해야 한다고 칩시다.
dictionary["Count"]++;
위의 동작은
dictionary["Count"] = dictionary["Count"] + 1;
이 되고 키 인덱싱을 두번 해야 하고 심지어 이 행위를 100만번 반복해야 하죠.
이전에는 이 코드를 개선할 방법이 제약적이였습니다.
그런데 이제 CollectionsMarshal.GetValueRefOrAddDefault()
에 의해 가능해졌습니다.
using System.Runtime.InteropServices;
Dictionary<string, int> dictionary = new();
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, "Count", out bool exists);
for (var i = 0; i < 1000000; i++)
{
count++; // dictionary["Count"]의 값을 수정함
}
Console.WriteLine(dictionary["Count"]); // 1000000 출력됨