LINQ 쿼리에서 ToList () 또는 ToArray ()를 호출하는 것이 더 낫습니까?


518

나는 종종 내가 그것을 선언하는 곳에서 쿼리를 평가하고 싶은 경우에 빠진다. 내가 여러 번 반복해야하기 때문에 이것은 보통 는 계산할 비싸다. 예를 들면 다음과 같습니다.

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

이것은 잘 작동합니다. 그러나 결과를 수정하지 않을 경우 ToArray()대신을 호출 할 수도 있습니다 ToList().

그러나 나는 ToArray()첫 번째 호출로 구현 되는지 여부를 궁금해 ToList()하기 때문에 호출하는 것보다 메모리 효율성이 떨어집니다 ToList().

내가 미쳤어? ToArray()메모리가 두 번 할당되지 않는다는 지식을 안전하고 안전하게 전화해야합니까 ?


10
.NET에서 커튼 뒤에서 어떤 일이 발생하는지 알고 싶다면 .NET Reflector를
David Hedlund

32

1
중요한 관계가 있더라도 stackoverflow.com/questions/6750447/c-toarray-performance 가이 질문의 복제본 이라는 데 동의하지 않습니다 . 메모리 사용 (이 질문)과 성능 (다른 질문)은 흥미롭고 사소한 고려 사항입니다. 그것들은 개별적으로 설명 할 수 있지만 둘 다 서로를 선택하기로 결정해야합니다. 이 질문이나 다른 질문에 대한 답변 중 하나를 포괄적으로 추천 할 수는 없습니다. 함께 할 때 하나를 선택하는 방법에 대한 다소 완전한 토론을 제공하는 몇 가지 답변이 있습니다.
steve

1
@Gqqnbig-가장 유용한 의견! 감사합니다 :-)
Mark Cooper

답변:


365

다른 제약 조건을 충족시키기 위해 단순히 배열이 필요하지 않으면 사용해야합니다 ToList. 대부분의 시나리오에서 ToArray보다 더 많은 메모리를 할당합니다ToList 합니다.

둘 다 스토리지에 스토리지를 사용하지만 ToList보다 유연한 제약이 있습니다. 최소한 컬렉션의 요소 수만큼 배열이 커야합니다. 배열이 더 크면 문제가되지 않습니다. 하나ToArray 배열의 크기는 요소 수와 정확히 일치해야합니다.

이 제약 조건을 충족시키기 위해 ToArray종종보다 하나 이상의 할당을 수행 ToList합니다. 배열이 충분히 커지면 정확히 정확한 크기의 배열을 할당하고 요소를 해당 배열로 다시 복사합니다. 이것을 피할 수있는 유일한 시간은 배열에 대한 증가 알고리즘이 저장해야 할 요소의 수와 정확히 일치하는 경우입니다 (소수로).

편집하다

두 사람이 여분의 사용하지 않은 메모리를 갖는 결과에 대해 물었습니다. List<T> 값 .

이것은 유효한 관심사입니다. 생성 된 컬렉션이 오래 지속되고 생성 된 후에는 수정되지 않으며 Gen2 힙에 착륙 할 가능성이 높은 경우 추가 할당을 수행하는 것이 좋습니다.ToArray .

일반적으로 나는 이것이 드문 경우라고 생각합니다. 많은 것을 보는 것이 훨씬 일반적입니다.ToArray다른 짧은 수명의 메모리 사용으로 즉시 전달되는 호출ToList 입니다.

여기서 핵심은 프로파일 링, 프로파일 링 및 기타 프로파일 링입니다.


14
반면에, 배열을 생성하는 레거시에 할당 된 추가 메모리는 가비지 수집에 적합하지 않지만 목록에 대한 추가 오버 헤드는 남아 있지 않습니까? 나는 그것을 더 단순하게 말한다. 요소를 추가하거나 제거해야하는 경우이를위한 도구가 있습니다. 그렇지 않은 경우 다른 도구가 있습니다. 말이되는 것을 사용하십시오. 만약 나중에, 당신은 메모리 및 성능에 문제를 발견, 이것은 그것이 , 변경합니다.
Anthony Pegram

1
@AnthonyPegram 예, 유효한 고려 사항입니다. 값이 장기 저장소에 사용되고 수정되지 않고 잠재적으로 Gen 2로 만들 경우 Gen 2 힙을 오염시키는 것보다 추가 할당을 지불하는 것이 좋습니다. IME는 거의 보이지 않습니다. ToArray가 다른 짧은 수명의 LINQ 쿼리에 즉시 전달되는 것을 보는 것이 훨씬 일반적입니다.
JaredPar

2
@AnthonyPegram 나는 토론의 측면을 포함하는 내 대답을 업데이 트
JaredPar

8
@JaredPar 자동 스페어 위치 ToArray가있는 정확한 위치 크기가 필요한 경우 더 많은 메모리를 할당 할 수있는 방법을 이해하지 못합니다 ToList<>. (자동 증가)
Royi Namir

5
@Array는 ToArray가 먼저 오버 헤드로 ToList 스타일 할당을 수행 한 다음 정확한 추가 크기 할당을 수행하므로 @RoyiNamir가 필요합니다.
Timbo

169

List<T>동적 크기의 배열로 구현 되기 때문에 성능 차이는 중요하지 않습니다 . 중 하나를 호출 ToArray()(내부 사용하는 Buffer<T>배열을 성장 클래스) 또는 ToList()를 호출하는 (List<T>(IEnumerable<T>) 생성자)를 배열에 넣어 그것은 그들 모두를 맞을 때까지 배열을 성장의 문제가 끝나게됩니다.

이 사실을 구체적으로 확인하려면 Reflector에서 해당 메소드의 구현을 확인하십시오. 거의 동일한 코드로 요약됩니다.


2
내가 알게 된 흥미로운 사실은 프로젝션에서 그룹 조인을 통해 정의 된 그룹을 사용하여 발생하는 상관 쿼리에 대해 Linq to SQL이 다른 하위 쿼리를 추가하여 해당 그룹의 개수를 검색한다는 것입니다. 이것은이 경우 항목을 검색하기 전에 컬렉션의 크기를 알 수 있으므로 정확한 크기의 배열을 직접 생성하여 결과를 구체화하면서 처리 및 메모리 리소스를 절약 할 수 있다고 가정합니다.
jpierson

133
개수를 미리 알고 있으면 성능이 동일합니다. 그러나 Count를 미리 알 수없는 경우 ToArray()와 차이점 ToList()은 전자가 과잉을 다듬어야한다는 것입니다. 즉, 전체 배열을 복사해야하지만 후자는 과잉을 다듬지는 않지만 평균 25를 사용합니다. 더 많은 메모리. 데이터 유형이 큰 경우에만 영향을 미칩니다 struct. 그냥 생각할 음식.
Scott Rippey

9
@EldritchConundrum 25 %는이 논리에서 비롯됩니다. 항목 수를 알 수없는 경우 작은 버퍼를 만들어서 호출 ToList하거나 ToArray시작합니다. 해당 버퍼가 채워지면 버퍼 용량이 두 배가되고 계속됩니다. 용량이 항상 두 배이기 때문에 사용하지 않는 버퍼는 항상 0 %에서 50 % 사이입니다.
Scott Rippey

2
@ScottRippey 방금 IEnumerable 소스에서 새 List의 소스를 찾아서 IEnumerable이 ICollection인지 확인한 다음 그렇다면 Count 속성에 필요한 정확한 크기로 하나의 배열을 할당하여 시작합니다. ToList ()가 확실히 더 빠를 것입니다. 가장 일반적인 경우는 아니지만 전체 답변에는 해당 사실이 포함될 수 있습니다.
AndyClaw

3
모두를 @AndyClaw List하고 Buffer확인합니다 ICollection이 경우 성능은 동일합니다.
Scott Rippey

54

(7 년 후 ...)

몇 가지 다른 (좋은) 답변은 발생할 미세한 성능 차이에 집중했습니다.

이 게시물은 배열 ( )에 의해 생성 된 것 사이에 존재 하는 의미 론적 차이 를 언급 한 보충 자료입니다 .IEnumerator<T>T[]List<T> .

예제로 가장 잘 설명되어 있습니다.

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}

위의 코드는 예외없이 실행되며 출력을 생성합니다.

1
2
삼
4
5
6
7
8
900
10

이는 열거자가 IEnumarator<int>반환 한 int[]후 배열이 수정되었는지 여부를 추적하지 않음을 나타냅니다 .

로컬 변수를 source로 선언 했습니다 IList<int>. 그런 식으로 C # 컴파일러가 foreach명령문을 for (var idx = 0; idx < source.Length; idx++) { /* ... */ }루프 와 동등한 것으로 최적화하지 않도록 합니다. 이것은 var source = ...;대신 사용하면 C # 컴파일러가 할 수있는 일 입니다. 현재 사용중인 .NET 프레임 워크 버전에서 여기에 사용 된 실제 열거자는 비공개 참조 유형 System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]이지만 물론 구현 세부 사항입니다.

이제 .ToArray()로 변경 하면 다음 .ToList()과 같은 결과 만 얻을 수 있습니다.

1
2
삼
4
5

a로 다음 System.InvalidOperationException말 블로우 업 :

수집이 수정되었습니다. 열거 작업이 실행되지 않을 수 있습니다.

이 경우 기본 열거자는 public mutable value-type입니다 System.Collections.Generic.List`1+Enumerator[System.Int32]( IEnumerator<int>이 경우 상자 안에 상자가 있으므로 사용 IList<int>).

결론적으로, keep에 의해 생성 된 열거자는 열거List<T>동안리스트가 변경되는지 여부를 추적하지만,에 의해 생성 된 열거자는T[]그렇지 않습니다. .ToList()와사이에서 선택할 때이 차이점을 고려하십시오.ToArray().

사람들은 종종 열거 자의 수명 동안 수정되었는지 여부를 추적하는 컬렉션을 추가 .ToArray() 하거나 .ToList()회피하기 위해 하나를 추가 합니다.

(사람이 알고 싶은 경우 방법 (가) List<>수집이 수정되었는지 여부에 대한 추적, 개인 필드가 _version매번 변경되는이 클래스에 List<>업데이트됩니다.)


28

성능 차이가 중요하지 않다는 @mquander에 동의합니다. 그러나 나는 그것을 확신하기 위해 벤치마킹하고 싶었습니다.

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

각 소스 배열 / 목록에는 1000 개의 요소가 있습니다. 따라서 시간과 메모리의 차이가 무시할 만하다는 것을 알 수 있습니다.

내 결론 : 몇 바이트의 메모리가 실제로 중요하지 않으면 배열보다 더 많은 기능을 제공하기 때문에 ToList ()를 사용할 수도 List<T>있습니다.


1
struct기본 유형이나 클래스 대신 큰 것을 사용하면이 결과가 다른지 궁금합니다 .
Scott Rippey

12
List <T> .ToList ???? 무슨 의미 요? IEnumerable을 제공하는 것이 좋습니다 .ICollection 인터페이스를 구현하지는 않습니다.
Grigory

8
나는 ToList또는 의 시간 만 측정 ToArray하고 어떤 열거도 측정하지 않도록하고 싶었습니다 IEnumerable. List <T> .ToList ()는 여전히 새 List <T>를 작성합니다. 단순히 "반환"하지 않습니다.
EMP

23
-1 매개 변수 가 제공 될 때 의 동작 ToArray()및 동작이 ToList()너무 다름 ICollection<T>-단일 할당 및 단일 복사 작업 만 수행합니다. 모두 List<T>Array구현 ICollection<T>하므로 벤치 마크가 전혀 유효하지 않습니다.
Mohammad Dehghan

1
관심있는 사람은 누구나 저의 벤치 마크를 별도의 답변으로 게시했습니다 . 그것은 구현 문제 .Select(i => i)를 피하기 위해 사용 되며, 처음부터 ICollection<T>소스 IEnumerable<>를 반복하는 데 얼마나 많은 시간이 걸리는지 확인하는 제어 그룹을 포함합니다 .
StriplingWarrior

19

ToList()IEnumerable<T>(예를 들어 ORM에서) 사용하는 경우 일반적으로 선호됩니다 . 처음에 시퀀스 길이를 모르는 경우 ToArray()List와 같은 동적 길이 컬렉션을 만든 다음 배열로 변환하면 시간이 더 걸립니다.


26
이 경우 가독성이 성능을 능가한다고 결정했습니다. 요소를 계속 추가 할 때 ToList 만 사용합니다. 다른 모든 경우 (대부분의 경우)에는 ToArray를 사용합니다. 그러나 입력 주셔서 감사합니다!
Frank Krueger

5
ILSpy를보고을 Enumerable.ToArray()호출합니다 new Buffer<TSource>(source).ToArray(). 버퍼 생성자에서 소스가 ICollection을 구현하는 경우 source.CopyTo (items, 0)를 호출 한 다음 .ToArray ()는 내부 항목 배열을 직접 반환합니다. 따라서이 경우 추가 시간이 걸리는 전환이 없습니다. 소스가 ICollection을 구현하지 않으면 위의 Scott Rippey의 설명에 설명 된대로 ToArray는 배열의 끝에서 사용되지 않는 추가 위치를 잘라 내기 위해 배열 사본을 생성합니다.
BrandonAGr

19

메모리는 항상 두 번 또는 그에 가까운 것으로 할당됩니다. 배열의 크기를 조정할 수 없으므로 두 방법 모두 일종의 메커니즘을 사용하여 증가하는 컬렉션에서 데이터를 수집합니다. 글쎄, 목록 자체가 점점 커지고 있습니다.

이 목록은 어레이를 내부 스토리지로 사용하며 필요한 경우 용량을 두 배로 늘립니다. 즉, 항목의 평균 2/3가 최소 한 번 재 할당되고, 적어도 두 번 재 할당 된 것의 절반, 적어도 세 번 이상은 재 할당 된 것입니다. 즉, 각 항목은 평균 1.3 배로 재 할당되었으므로 오버 헤드가 그리 크지 않습니다.

또한 문자열을 수집하는 경우 컬렉션 자체에는 문자열에 대한 참조 만 포함되며 문자열 자체는 재 할당되지 않습니다.


이것은 무지한 질문 일지 모르지만 개요의 2/3, 1/3, 1/6 논리가 List의 배열을 확장 할 수 있다고 가정하지 않습니까? 즉, 배열 끝에 여유 공간이있어 기존 할당을 이동할 필요가 없습니까?

@JonofAllTrades : 아니요, 배열은 확장되지 않으며 .NET의 메모리 관리는 그렇게하지 않습니다. 제자리에 확장된다면 아이템을 재 할당 할 필요가 없습니다.
Guffa

아, 알다시피 : 재 할당되지 않은 항목은 최종 할당에 있었기 때문에 그렇게 할 필요가 없었습니다. 이전 할당에서 할당 된 모든 항목이 이동되지만 배열 길이의 로그 증가로 인해 계산 가능한 부분입니다. 설명해 주셔서 감사합니다!

19

2020 외부에 있으며 모두가 .NET Core 3.1을 사용하고 있으므로 Benchmark.NET으로 벤치 마크를 실행하기로 결정했습니다.

TL; DR : ToArray ()는 성능면에서 더 좋으며 컬렉션을 변경하지 않으려는 경우 더 나은 작업 전달 의도를 수행합니다.


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

결과는 다음과 같습니다.


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

1
컬렉션을 변경하지 않으려 ToImmutableArray()는 경우 System.Collections.Immutable 패키지에서 의도를 더 잘 나타낼 수 있다고 생각합니다.
Arturo Torres Sánchez

@ ArturoTorresSánchez true이지만 컬렉션이 메서드 외부에 노출되지 않으면 배열을 사용합니다.
Tyrrrz

2
고마워 선택한 답변은 단순한 논쟁이며 그 논쟁에 따른 결과를 가정합니다. 과학적으로나 보너스 로 차이가 얼마나 많은지 알기 위해서는 알 수있는 유일한 방법이 있습니다.
조나스

15

편집 :이 답변의 마지막 부분이 유효하지 않습니다. 그러나 나머지는 여전히 유용한 정보이므로 남겨 두겠습니다.

나는 이것이 오래된 게시물이라는 것을 알고 있지만 같은 질문을하고 연구를 한 후에 공유 할 가치가있는 흥미로운 것을 발견했습니다.

먼저 @mquander와 그의 답변에 동의합니다. 그는 성능 측면에서 두 사람이 동일하다고 말하는 것이 맞습니다.

그러나 Reflector를 사용하여 System.Linq.Enumerable확장 네임 스페이스 의 메서드를 살펴 보았으며 매우 일반적인 최적화를 발견했습니다.
가능할 때마다 IEnumerable<T>소스는 분석법으로 캐스팅 IList<T>되거나 ICollection<T>최적화됩니다. 예를 들어를보십시오 ElementAt(int).

흥미롭게도 Microsoft는에 대해서만 최적화를 선택 IList<T>했지만에 대해서는 최적화 하지 않았습니다 IList. Microsoft가 IList<T>인터페이스를 선호하는 것 같습니다 .

System.Array만 구현 IList하므로 이러한 확장 최적화의 이점은 없습니다.
따라서 최선의 방법은이 .ToList()방법 을 사용하는 것입니다.
확장 방법 중 하나를 사용하거나 다른 방법으로 목록을 전달하면이 확장 방법이에 최적화 될 수 있습니다 IList<T>.


16
나는 시험을하고 놀라운 것을 발견했다. 배열은 IList <T>를 구현합니다! Reflector를 사용하여 System.Array를 분석하면 IList, ICollection, IEnumerable의 상속 체인 만 표시되지만 런타임 리플렉션을 사용하여 string []에 IList, ICollection, IEnumerable, IList <string>, ICollection <string의 상속 체인이 있음을 알았습니다. >, IEnumerable <문자열>. 따라서 @mquander보다 더 나은 대답이 없습니다!
Scott Rippey

@ScottRippey 예. 당신이 알아 차린 이상한 관찰은 실제로 "해킹"의 일부이며, "고정 크기"및 유사한 속성 (캐스팅 방법에 따라 약간의 불일치가 있음)과 관련하여 다소 이상한 영향을 미칩니다. .net 소스 코드 내부에서이 주제를 다루는 상당히 큰 주석이 있습니다. 링크하지 않아서 죄송하지만 올바르게 기억하면 쉽게 찾을 수 있습니다 (배열 클래스 내부). (그리고 불일치를 논의 큰 SO 질문은 .... 또한 어딘가에 ...> __>가)
AnorZaken

@ScottRippey 단지 참고로 귀하의 의견과 관련된이 답변을 찾았습니다 : stackoverflow.com/a/4482567/2063755
David Klempfner

14

사람들이 여기서 수행 한 다른 벤치 마크가 부족하다는 것을 알았으므로 여기에 내 균열이 있습니다. 내 방법론에 문제가 있으면 알려주십시오.

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion

LINQPad 스크립트는 여기에서 다운로드 할 수 있습니다 .

결과 : ToArray와 ToList 성능

위의 코드를 조정하면 다음을 발견 할 수 있습니다.

  1. 더 작은 배열을 다룰 때는 차이가 덜 중요 합니다 . 더 많은 반복, 더 작은 배열
  2. ints보다는 s를 처리 할 때 차이가 덜 중요 string합니다.
  3. structs 대신 큰을 사용하면 string일반적으로 시간이 많이 걸리지 만 비율을 크게 바꾸지는 않습니다.

이것은 최고 투표 답변의 결론에 동의합니다.

  1. 코드에서 많은 양의 데이터 목록을 자주 생성하지 않는 한 성능 차이가 눈에 띄지 않을 것입니다. (100K 문자열 목록을 각각 1000 개 만들 때 200ms 만 차이가있었습니다.)
  2. ToList() 일관되게 빠르게 실행되며 오랫동안 결과에 매달리지 않으려는 경우 더 나은 선택입니다.

최신 정보

@JonHanna는의 구현에 따라 지적 SelectA의 그것을하는 것이 가능 ToList()또는 ToArray()실행 시간의 결과 집합의 크기 앞서를 예측. .Select(i => i)위의 코드를 대체 Where(i => true) 하면 현재 매우 유사한 결과얻을 수 있으며 .NET 구현에 관계없이 그렇게 할 가능성이 높습니다.

선택 대신 위치를 사용하는 벤치 마크


그것은 크기가 될 것입니다 실현 것이기 때문에 .NET 코어에서 두 경우 모두 더 나은 여기으로 netfx보다해야 100000하고 최적화하는 데 사용하는 모두 ToList()ToArray()함께, ToArray()그것은 필요가 축소 작업을 필요로하지 않기 때문에 아주 약간 가벼운 것 그렇지 않으면 어느 곳 ToList()이 유리합니다. Where이러한 크기 예측을 수행 할 수 없기 때문에 문제의 예는 여전히 잃어 버릴 것입니다.
Jon Hanna

@ JonHanna : 빠른 피드백 감사합니다. .NET Core가 그 최적화를하고 있는지 몰랐습니다. 멋지다. 내 코드에서 이를 수정하기 위해 .Select(i => i)바꿀 수 있습니다 .Where(i => true).
StriplingWarrior

예, corefx에서 최적화에 영향을 미치는 최적화가 중지됩니다. ToArray()위와 같이 2의 거듭 제곱 크기 ( 장점을 제공해야 함 )와 그렇지 않은 크기를 모두 갖고 결과를 비교하는 것이 흥미로울 수 있습니다 .
존 한나

@JonHanna : 흥미롭게도 최상의 시나리오 에서는 ToArray() 여전히 잃 습니다. Math.Pow(2, 15)요소를 사용하면 (ToList : 700ms, ToArray : 900ms)입니다. 하나 이상의 요소를 추가하면 (ToList : 925, ToArray : 1350)에 충돌합니다. ToArray배열이 이미 완벽한 크기 일지라도 배열을 복사하고 있는지 궁금합니다 . 그들은 아마도 추가 조건부 가치가없는 드문 충분한 사건이라고 생각했을 것입니다.
StriplingWarrior

corefx에서 최적화를 시작하기 전에도 정확한 크기 일치로 복사하지 않았으므로 가장 많이 나누는 경우입니다.
존 한나

12

당신은 당신의 결정에 기초해야합니다 ToList 하거나 ToArray이상적으로 디자인 선택이 무엇을 기반으로합니다. 색인으로 만 반복하고 액세스 할 수있는 모음을 원하면을 선택하십시오 ToArray. 나중에 번거 로움없이 컬렉션에서 추가 및 제거하는 추가 기능을 원한다면ToList 실제로 배열에 추가 할 수는 없지만 일반적으로 올바른 도구는 아닙니다.

성능이 중요한 경우 작업 속도가 더 빠른 것을 고려해야합니다. 실제로 전화를 걸 ToList거나 ToArray백만 번을받지 않지만 획득 한 컬렉션에 대해 백만 번 작업 할 수 있습니다 . 그 점 에서 약간의 오버 헤드 가 []있기 때문에 더 좋습니다 . 효율성 비교에 대해서는이 스레드를 참조하십시오. 어느 것이 더 효율적인지 : List <int> 또는 int []List<>[]

얼마 전 내 자신의 테스트에서 ToArray더 빨리 발견했습니다 . 그리고 테스트가 얼마나 왜곡되었는지 잘 모르겠습니다. 그러나 성능 차이는 그다지 중요하지 않으므로 이러한 쿼리를 수백만 번 반복하여 실행하는 경우에만 눈에.니다.


2
예-컴파일러가 IEnumerable <>이 아닌 배열을 반복한다는 것을 알고 있으면 반복을 크게 최적화 할 수 있습니다.
RobSiklos

12

매우 늦은 답변이지만 Google 직원에게 도움이 될 것이라고 생각합니다.

그들은 linq를 사용하여 만들었을 때 둘 다 빨다. 둘 다 필요한 경우 버퍼 크기조정 하기 위해 동일한 코드를 구현 합니다 . ToArray내부적 IEnumerable<>으로 4 개의 요소 배열을 할당하여 클래스를 사용 하여 배열 로 변환 합니다. 충분하지 않으면 새 배열을 만들어 현재 배열의 크기를 두 배로 늘리고 현재 배열을 복사하여 크기를 두 배로 늘리십시오. 마지막으로 항목의 새로운 개수의 배열을 할당합니다. 쿼리에서 129 개의 요소를 반환하면 ToArray는 6 개의 할당 및 메모리 복사 작업을 수행하여 256 개의 요소 배열을 만들고 129의 다른 배열을 반환합니다. 메모리 효율성을 위해 너무 많은.

ToList는 동일한 작업을 수행하지만 나중에 항목을 추가 할 수 있으므로 마지막 할당을 건너 뜁니다. linq 쿼리에서 생성되거나 수동으로 생성 된 목록은 신경 쓰지 않습니다.

리스트는 메모리에서는 낫지 만 cpu에서는 나 빠진다.리스트는 일반적인 솔루션이기 때문에 모든 동작은 배열에 대한 .net의 내부 범위 점검 외에 범위 점검이 필요하다.

따라서 결과 집합을 너무 여러 번 반복하면 목록보다 범위 검사가 적기 때문에 배열이 좋으며 컴파일러는 일반적으로 순차적 액세스를 위해 배열을 최적화합니다.

목록을 작성할 때 용량 매개 변수를 지정하면 목록의 초기화 할당이 더 나을 수 있습니다. 이 경우 결과 크기를 알고 있다고 가정하면 배열을 한 번만 할당합니다. ToListlinq는 오버로드를 제공하기 위해 과부하를 지정하지 않으므로 주어진 용량으로 목록을 작성한 다음를 사용하는 확장 메소드를 작성해야합니다 List<>.AddRange.

이 답변을 마치려면 다음 문장을 작성해야합니다

  1. 마지막으로 ToArray 또는 ToList를 사용할 수 있습니다. 성능은 크게 다르지 않습니다 (@ EMP 답변 참조).
  2. C #을 사용하고 있습니다. 성능이 필요한 경우 고성능 코드 작성에 대해 걱정하지 말고 성능 코드 불량에 대해 걱정하지 마십시오.
  3. 고성능 코드의 경우 항상 x64를 대상으로합니다. AFAIK, x64 JIT는 C ++ 컴파일러를 기반으로하며 꼬리 재귀 최적화와 같은 재미있는 작업을 수행합니다.
  4. 4.5를 사용하면 프로파일 가이드 최적화 및 멀티 코어 JIT를 즐길 수 있습니다.
  5. 마지막으로 async / await 패턴을 사용하여 더 빠르게 처리 할 수 ​​있습니다.

둘 다 빨다? 여분의 메모리 할당이 필요없는 대체 아이디어가 있습니까?
nawfal

의문의 맥락에서, 예, 그들은 중복 할당 때문에 빠르지 만 다른 것은 없습니다. 중복 할당을 줄이려면 메모리와 반복 속도를 희생하면서 연결된 목록을 사용할 수 있습니다. 하루가 끝나면 이것이 우리가하는 일이며, 우리는 트레이드 오프를합니다. 용량이 200 인 목록 (예 :)을 만든 다음 항목을로드 할 경우 다른 아이디어입니다. 이렇게하면 중복성이 줄어들지 만 어레이는 항상 빠르기 때문에 또 다른 단점입니다.
Erdogan Kurtur

200 목록을 만드 시겠습니까? 즉 크기를 조정하지 않도록,하지만 난 사용하는 중복 메모리에 대해 이야기했다. 크기에 대한 사전 지식이 없으므로 도움을 줄 수 없습니다. 의 생성자에서 이미 용량을 지정할 List<T>수 있지만, 할 수 없거나 할 수없는 경우에는 도움을 줄 수 없습니다.
nawfal

2
메모리에있는 중복 데이터는 포인터 목록 (이 경우) 인 배열의 내용입니다. 백만 개의 64 비트 포인터는 8MB의 메모리를 사용하는데, 이는 백만 개의 객체와 비교할 수 없습니다. 200은 숫자에 불과하며 크기 조정 호출 수를 최대 5 번 줄일 수 있습니다. 그렇습니다, 우리는 그것을 도울 수 없습니다. 더 나은 옵션이 없습니다. 더 나은 해결책은 없지만 문제가 어디에 있는지 말할 수 없다는 것을 의미하지는 않습니다.
Erdogan Kurtur

1
흠 결국 선을 그리는 곳입니다. 나는 현재 구현을 좋아한다. 당신의 대답의 톤은 그것이 문제가 아니라 비평이라고 생각하게 만들었습니다 :)
nawfal

7

이것은 오래된 질문이지만 그것을 우연히 본 사용자의 이익을 위해 Enumerable 'Memoizing'에 대한 대안이 있습니다. 목록 또는 배열의 컬렉션 속성이 사용되지 않더라도 ToList ()는 많이 사용됩니다.

메모는 RX / System.Interactive lib에서 사용할 수 있으며 여기에 설명되어 있습니다. System.Interactive를 사용한 추가 LINQ

( Linq와 함께 작업하는 경우 많이 권장 되는 Bart De'Smet의 블로그 에서 Objects까지)


4

한 가지 옵션은 읽기 전용 을 반환하는 자체 확장 메서드를 추가하는 것 ICollection<T>입니다. 이것은 사용하는 것보다 더 좋을 수 있습니다 ToList또는 ToArray당신이 배열 / 목록의 색인 속성 중 하나를 사용하거나 목록에서 / 제거를 추가하지 않으려는 경우.

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}

단위 테스트 :

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}

읽기 전용 콜렉션 계약은 오브젝트의 사용자가 오브젝트를 수정할 수 없다고 규정하지만 소유자가 변경 가능한 인터페이스를 제공하는 오브젝트를 참조하면 여전히 그렇게 할 수 있습니다. 기본 구조가 절대 변경되지 않도록 보장하는 인터페이스의 경우 변경 불가능한 콜렉션을보십시오. 불변, 읽기 전용 또는 일반 읽기 / 쓰기 컬렉션이 더 나은 이유에 대해서는 비교할 기준점이 필요합니다. 궁극적 인 대답은 없습니다 (그렇지 않으면 선택할 필요가 없습니다).
tne

@tne 참고 AsReadOnly 전에 Tolist를 수행하므로 기본 변경 가능에 대한 참조가 없습니다.
weston

당신은 전적으로 옳았으며, 아마도 불변의 컬렉션이 BCL에 오기 전에 일을하는 가장 좋은 방법 일 것입니다 (당신의 대답 한 달 후 첫 번째 베타 버전이 나왔습니다).
tne

스레드 안전성을 위해 불변의 컬렉션이 존재하는데, 스레드는 스레드가 변경되지 않을 것이라고 가정 할 수 있으며, 변경 될 경우 독자와 경쟁하여 사용하는 동안 변경하지 않고 새 버전이 작성됩니다. 이런 식으로 아무도 잠금을 획득 할 필요가 없습니다.
doug65536

4

ToListAsync<T>() 선호됩니다.

Entity Framework 6에서 두 메소드는 결국 동일한 내부 메소드를 ToArrayAsync<T>()호출 하지만 list.ToArray()끝에 호출합니다 .

T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;

따라서 ToArrayAsync<T>()약간의 오버 헤드가 있으므로 ToListAsync<T>()선호됩니다.


1
그것은 실제로 내가 찾던 답입니다. 어떻게 EF가 그것을합니까? EF Core에서 어떻게 작동하는지 궁금합니다.
Shimmy Weitzhandler

3

오래된 질문이지만 항상 새로운 질문자.

의 소스에 따르면 System.Linq.Enumerable , ToList단지를 반환 new List(source)하면서, ToArray사용 A는 new Buffer<T>(source).ToArray()을 반환 T[].

메모리 할당 정보 :

IEnumerable<T>유일한 개체에서 실행되는 동안 ToArray메모리를 한 번 이상 할당하십시오 ToList. 그러나 GC는 필요할 때 가비지 수집을 수행하므로 대부분의 경우 신경 쓰지 않아도됩니다.

효율적인 런타임 정보 :

이 질문에 답하는 사람들은 자신의 컴퓨터에서 다음 코드를 실행할 수 있으며 답을 얻을 수 있습니다.

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}

내 컴퓨터에서 다음 결과를 얻었습니다.

그룹 1 :

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

그룹 2 :

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms

그룹 3 :

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms

그룹 4 :

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

그룹 5 :

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

응답의 문자 수에 대한 스택 오버플로 제한으로 인해 Group2 및 Group3의 샘플 목록이 생략됩니다.

보시다시피, ToList또는 ToArry대부분의 경우 사용하는 것이 중요하지 않습니다 .

실행 시간 계산 처리 동안 IEnumerable<T>계산 가져온로드 메모리 할당보다 무거워의 동작 복사하면, 오브젝트 ToListToArray의 차이는 크지이다 ( C.ToList vs C.ToArrayS.ToList vs S.ToArray).

이 차이는 런타임 계산되지 않은 IEnumerable<T>객체 ( C1.ToList vs C1.ToArrayS1.ToList vs S1.ToArray) 에서만 관찰 할 수 있습니다 . 그러나 절대 차이 (<60ms)는 백만 개의 작은 물체에서 여전히 허용됩니다 IEnumerable<T>. 사실, 차이의 구현에 의해 결정 Enumerator<T>IEnumerable<T>. 따라서 프로그램이 실제로 정말로 민감한 경우 프로파일, 프로파일, 프로파일작성 해야 합니다 ! 마지막으로 병목 현상이 ToList또는 ToArray에 있지만 열거 자의 세부 사항 이 아님을 알 수 있습니다.

그리고, 결과 C2.ToList vs C2.ToArrayS2.ToList vs S2.ToArray, 당신이 정말로 걱정 할 필요가 없습니다 쇼 ToList또는 ToArray에 비 런타임 계산ICollection<T> 객체.

물론 이것은 내 컴퓨터의 결과이며 다른 컴퓨터에서 이러한 작업의 실제 시간은 동일하지 않습니다. 위의 코드를 사용하여 컴퓨터에서 찾을 수 있습니다.

선택해야 할 유일한 이유 는 @Jeppe Stig Nielsen 의 답변에 설명 된대로 List<T>또는에 특정 요구 T[]있기 때문 입니다.


1

이 결과를 사용하는 데 관심이있는 사람은 다음과 같은 다른 Linq-to-sql

from q in context.MyTable
where myListOrArray.Contains(q.someID)
select q;

myListOrArray에 List 또는 Array를 사용했는지 여부에 관계없이 생성 된 SQL은 동일합니다. 이제 일부 사람들 이이 문 앞에 열거하는 이유를 물을 수도 있지만 IQueryable과 (List 또는 Array)에서 생성 된 SQL에는 차이가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.