É melhor chamar ToList () ou ToArray () em consultas LINQ?

519

Frequentemente, encontro o caso em que quero avaliar uma consulta exatamente onde a declaro. Isso geralmente ocorre porque eu preciso iterá-lo várias vezes e é caro calcular. Por exemplo:

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

Isso funciona bem. Mas se não vou modificar o resultado, é melhor ligar em ToArray()vez de ToList().

Gostaria de saber, no entanto, se ToArray()é implementado pela primeira chamada ToList()e, portanto, é menos eficiente de memória do que apenas a chamada ToList().

Eu sou louco? Devo apenas ligar ToArray()- em segurança, sabendo que a memória não será alocada duas vezes?

Frank Krueger
fonte
10
Se você quiser descobrir o que acontece por trás das cortinas em .NET, eu realmente recomendo .NET Reflector
David Hedlund
32
@DavidHedlund Eu recomendo o código-fonte .net .
Gqqnbig
1
Não concordo que o stackoverflow.com/questions/6750447/c-toarray-performance seja uma duplicata desta pergunta, mesmo que exista um relacionamento importante. Tanto o uso da memória (esta questão) quanto o desempenho (outra questão) são considerações interessantes e não triviais. Eles podem ser descritos separadamente, mas ambos devem levar em consideração a decisão de escolher um sobre o outro. Não posso recomendar nenhuma das respostas para essa ou outra pergunta como abrangente. Existem várias respostas que, juntas, fornecem uma discussão bastante completa de como escolher uma sobre a outra.
19416 steve
1
@Gqqnbig - comentário mais útil que nunca! Obrigado :-)
Mark Cooper

Respostas:

366

A menos que você simplesmente precise de uma matriz para atender a outras restrições que deve usar ToList. Na maioria dos cenários ToArray, alocará mais memória que ToList.

Ambos usam matrizes para armazenamento, mas ToListtêm uma restrição mais flexível. Ele precisa que a matriz seja pelo menos tão grande quanto o número de elementos na coleção. Se a matriz for maior, isso não é um problema. No entanto, ToArrayprecisa que o array seja dimensionado exatamente para o número de elementos.

Para atender a essa restrição, ToArraymuitas vezes é necessário mais uma alocação do que ToList. Depois de ter uma matriz grande o suficiente, ela aloca uma matriz exatamente do tamanho correto e copia os elementos de volta para essa matriz. O único momento em que isso pode ser evitado é quando o algoritmo de crescimento da matriz coincide com o número de elementos que precisam ser armazenados (definitivamente em minoria).

EDITAR

Algumas pessoas me perguntaram sobre a consequência de ter uma memória extra não utilizada no List<T>valor.

Esta é uma preocupação válida. Se a coleção criada tiver vida longa, nunca for modificada após a criação e tiver uma grande chance de aterrissar no heap Gen2, é melhor aproveitar a alocação extra ToArrayantecipadamente.

Em geral, embora eu ache que este é o caso mais raro. É muito mais comum ver muitas ToArraychamadas que são imediatamente passadas para outros usos de memória de curta duração, caso em que ToListé comprovadamente melhor.

A chave aqui é o perfil, perfil e, em seguida, perfil um pouco mais.

JaredPar
fonte
14
Por outro lado, a memória extra alocada para o trabalho braçal de criar a matriz não seria elegível para coleta de lixo, enquanto a sobrecarga extra da Lista permaneceria? Eu digo para simplificar. Se você precisar adicionar ou remover elementos, existe uma ferramenta para isso. Caso contrário, existe uma ferramenta diferente para isso. Use o que faz sentido. Se, mais tarde, você descobrir um problema de memória e desempenho, e é isso , altere-o.
Anthony Pegram
1
@AnthonyPegram sim, é uma consideração válida a ser feita. Se o valor estiver sendo usado no armazenamento de longo prazo, não for modificado e, potencialmente, entrar na geração 2, será melhor pagar a alocação extra agora do que poluir o heap da geração 2. IME, embora eu raramente veja isso. É muito mais comum ver o ToArray sendo passado imediatamente para outra consulta LINQ de curta duração.
JaredPar
2
@AnthonyPegram eu atualizei a minha resposta para incluir este lado da discussão
JaredPar
8
@JaredPar Eu não entendo como ToArrayPode alocar mais memória se precisar do tamanho exato dos locais, onde ToList<>obviamente tem os seus locais de reposição automáticos. (autoincrease)
Royi Namir
5
@RoyiNamir porque o ToArray primeiro faz as alocações no estilo ToList com sobrecarga e, em seguida, faz uma alocação de tamanho exato adicional.
Timbo
169

A diferença de desempenho será insignificante, pois List<T> é implementada como uma matriz de tamanho dinâmico. Chamar qualquer um ToArray()(que usa uma Buffer<T>classe interna para aumentar a matriz) ou ToList()(que chama o List<T>(IEnumerable<T>)construtor) acabará sendo uma questão de colocá-los em uma matriz e aumentar a matriz até que ela se ajuste a todos.

Se você deseja confirmação concreta desse fato, confira a implementação dos métodos em questão no Reflector - você verá que eles se resumem a um código quase idêntico.

mqp
fonte
2
Um fato interessante que me deparei é que, para consultas correlacionadas causadas pelo uso de um grupo definido por meio de uma junção de grupo em sua projeção, o Linq to SQL adiciona outra subconsulta para recuperar a contagem desse grupo. Suponho que, nesses casos, o tamanho da coleção seja conhecido antes que os itens sejam recuperados e, portanto, uma matriz de tamanho exato pode ser criada diretamente, economizando recursos de processamento e memória enquanto materializa os resultados.
jpierson
133
Se o Conde é conhecido antecipadamente, o desempenho é idêntico. No entanto, se o Count não é conhecido antecipadamente, a única diferença entre ToArray()e ToList()é que o primeiro precisa aparar o excesso, o que envolve copiar toda a matriz, enquanto o segundo não aparar o excesso, mas usa uma média de 25 % mais memória. Isso só terá implicações se o tipo de dados for grande struct. Apenas comida para pensar.
Scott Rippey
9
@EldritchConundrum 25% vem dessa lógica: se o número de itens for desconhecido, a chamada ToListou ToArraycomeçará criando um pequeno buffer. Quando esse buffer é preenchido, ele dobra a capacidade do buffer e continua. Como a capacidade é sempre duplicada, o buffer não utilizado estará sempre entre 0% e 50%.
22813 Scott Rippey
2
@ScottRippey Acabei de procurar a fonte da nova lista da fonte IEnumerable e verifica se o IEnumerable é um ICollection e, se for, começa alocando uma matriz com o tamanho exato necessário da propriedade Count. seria o caso em que ToList () seria definitivamente mais rápido. Uma resposta completa pode incluir esse fato, embora eu não ache que seja o caso mais comum.
AndyClaw
3
@AndyClaw Ambos Liste Bufferverificarão ICollection; nesse caso, o desempenho será idêntico.
Scott Rippey
54

(sete anos depois ...)

Algumas outras (boas) respostas se concentraram nas diferenças microscópicas de desempenho que ocorrerão.

Este post é apenas um complemento para mencionar a diferença semântica existente entre a IEnumerator<T>produzida por uma matriz ( T[]) em comparação com a retornada por a List<T>.

Melhor ilustrado com o exemplo:

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);
}

O código acima será executado sem exceção e produz a saída:

1
2
3
4
5
6
7
8
900
10

Isso mostra que o IEnumarator<int>retorno de um int[]não acompanha se a matriz foi modificada desde a criação do enumerador.

Observe que eu declarei a variável local sourcecomo um IList<int>. Dessa maneira, asseguro-me de que o compilador C # não otimize a foreachinstrução para algo equivalente a um for (var idx = 0; idx < source.Length; idx++) { /* ... */ }loop. Isso é algo que o compilador C # pode fazer se eu usar var source = ...;. Na minha versão atual do .NET framework, o enumerador real usado aqui é um tipo de referência não pública, System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]mas é claro que esse é um detalhe de implementação.

Agora, se eu mudar .ToArray()para .ToList(), eu só começar:

1
2
3
4
5

seguido de uma System.InvalidOperationExceptionafirmação explícita:

A coleção foi modificada; operação de enumeração pode não ser executada.

O enumerador subjacente nesse caso é o tipo de valor mutável público System.Collections.Generic.List`1+Enumerator[System.Int32](dentro de uma IEnumerator<int>caixa nesse caso porque eu uso IList<int>).

Em conclusão, o enumerador produzido por aList<T>controla se a lista é alterada durante a enumeração, enquanto o enumerador produzido porT[]não. Portanto, considere essa diferença ao escolher entre.ToList()e.ToArray().

As pessoas costumam adicionar um extra .ToArray() ou .ToList()para contornar uma coleção que controla se ela foi modificada durante a vida útil de um enumerador.

(Se alguém quiser saber como a empresa List<>controla se a coleção foi modificada, há um campo particular _versionnessa classe que é alterado toda vez que a List<>atualização é feita.)

Jeppe Stig Nielsen
fonte
28

Concordo com o @mquander que a diferença de desempenho deve ser insignificante. No entanto, eu queria compará-lo para ter certeza, então fiz - e é insignificante.

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

Cada matriz / lista de origem tinha 1000 elementos. Assim, você pode ver que as diferenças de tempo e memória são insignificantes.

Minha conclusão: você também pode usar ToList () , uma vez que a List<T>fornece mais funcionalidade que uma matriz, a menos que alguns bytes de memória realmente sejam importantes para você.

EMP
fonte
1
Gostaria de saber se esse resultado seria diferente se você usasse um structtipo ou classe grande em vez de primitivo.
Scott Rippey 20/05
12
Lista <T> .ToList ???? Que sentido ? É melhor tentar fornecer um IEnumerable, que não implementa a interface ICollection.
Grigory
8
Eu queria ter certeza de que estou medindo apenas o tempo da chamada ToListou ToArraye não a enumeração de nenhuma IEnumerable. List <T> .ToList () ainda cria uma nova lista <T> - não simplesmente "retorna isso".
EMP
23
-1 Os comportamentos ToArray()e ToList()diferem muito quando são fornecidos com um ICollection<T>parâmetro - Eles apenas fazem uma única alocação e uma única operação de cópia. Ambos List<T>e Arrayimplementar ICollection<T>, para que seus benchmarks não sejam válidos.
Mohammad Dehghan
1
Para quem estiver interessado, publiquei meu próprio benchmark como resposta separada . Ele é usado .Select(i => i)para evitar o ICollection<T>problema de implementação e inclui um grupo de controle para ver quanto tempo é gasto na iteração pela origem IEnumerable<>.
StriplingWarrior
19

ToList()geralmente é preferível se você o usar IEnumerable<T>(no ORM, por exemplo). Se o comprimento da sequência não for conhecido no início, ToArray()crie uma coleção de comprimento dinâmico como Lista e depois a converta em matriz, o que leva um tempo extra.

Vitaliy Ulantikov
fonte
26
Decidi que a legibilidade supera o desempenho neste caso. Agora, só uso o ToList quando espero continuar adicionando elementos. Em todos os outros casos (na maioria dos casos), eu uso o ToArray. Mas obrigado pela contribuição!
Frank Krueger
5
Procurando no ILSpy, Enumerable.ToArray()chamadas new Buffer<TSource>(source).ToArray(). No construtor Buffer, se a origem implementar ICollection, ele chama source.CopyTo (itens, 0) e, em seguida .ToArray () retorna a matriz de itens internos diretamente. Portanto, não há conversão que leve tempo extra nesse caso. Se a fonte não implementar ICollection, o ToArray resultará em uma cópia da matriz, a fim de aparar os locais extras não utilizados do final da matriz, conforme descrito pelo comentário de Scott Rippey acima.
BrandonAGr
19

A memória sempre será alocada duas vezes - ou algo próximo disso. Como você não pode redimensionar uma matriz, os dois métodos usarão algum tipo de mecanismo para reunir os dados em uma coleção crescente. (Bem, a lista é uma coleção crescente em si mesma.)

A lista usa uma matriz como armazenamento interno e dobra a capacidade quando necessário. Isso significa que, em média, 2/3 dos itens foram realocados pelo menos uma vez, metade dos que foram realocados pelo menos duas vezes, metade dos que pelo menos três vezes e assim por diante. Isso significa que cada item foi realocado em média 1,3 vezes, o que não representa muita sobrecarga.

Lembre-se também de que, se você estiver coletando cadeias, a própria coleção conterá apenas as referências às cadeias, as próprias cadeias não serão realocadas.

Guffa
fonte
Isso pode ser uma coisa ignorante, mas a lógica 2/3, 1/3, 1/6 que você descreve não assume que a matriz da Lista possa ser estendida no lugar? Ou seja, há espaço livre no final da matriz para que a alocação existente não precise ser movida?
@ JonofAllTrades: Não, a matriz nunca é estendida no local, o gerenciamento de memória no .NET simplesmente não faz isso. Se ele fosse estendido no local, não haveria necessidade de realocação dos itens.
Guffa
Ah, entendo: os itens que não são realocados não precisavam fazê-lo porque estavam na alocação final. Todos os itens alocados nas alocações anteriores são movidos, mas devido aos aumentos logarítmicos no comprimento da matriz, essa é uma fração calculável. Obrigado por esclarecer!
19

É 2020 lá fora e todo mundo está usando o .NET Core 3.1, então decidi executar alguns benchmarks com o Benchmark.NET.

TL; DR: ToArray () é melhor em termos de desempenho e faz um trabalho melhor ao transmitir intenções, se você não planeja alterar a coleção.


    [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>();
    }

Os resultados são:


    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)
Tyrrrz
fonte
1
Se você não planeja alterar a coleção, acho que a intenção pode ser melhor mostrada com ToImmutableArray()(do pacote System.Collections.Immutable) 😉
Arturo Torres Sánchez
@ ArturoTorresSánchez true, mas se a coleção não for exposta fora de um método, eu usaria apenas uma matriz.
Tyrrrz
2
Obrigado por isso. A resposta escolhida é um mero argumento e assume resultados após esse argumento. Para fazer isso cientificamente e, como bônus, saiba quanta diferença existe, existe apenas uma maneira real de saber.
Jonas
15

Editar : a última parte desta resposta não é válida. No entanto, o restante ainda é uma informação útil, então deixarei.

Sei que este é um post antigo, mas depois de ter a mesma pergunta e fazer algumas pesquisas, encontrei algo interessante que vale a pena compartilhar.

Primeiro, concordo com o @mquander e sua resposta. Ele está certo ao dizer que, em termos de desempenho, os dois são idênticos.

No entanto, tenho usado o Reflector para examinar os métodos no System.Linq.Enumerableespaço de nomes das extensões e notei uma otimização muito comum.
Sempre que possível, a IEnumerable<T>fonte é convertida para IList<T>ou ICollection<T>para otimizar o método. Por exemplo, olhe ElementAt(int).

Curiosamente, a Microsoft optou por otimizar apenas IList<T>, mas não IList. Parece que a Microsoft prefere usar a IList<T>interface.

System.Arrayimplementa apenas IList, portanto, não se beneficiará de nenhuma dessas otimizações de extensão.
Portanto, afirmo que a melhor prática é usar o método.ToList() método
Se você usar qualquer um dos métodos de extensão ou passar a lista para outro método, é possível que ele seja otimizado para um IList<T>.

Scott Rippey
fonte
16
Eu fiz um teste e descobri algo surpreendente. Uma matriz implementa IList <T>! Usando o Reflector para analisar o System.Array apenas revela uma cadeia de herança de IList, ICollection, IEnumerable, mas usando a reflexão em tempo de execução, descobri que string [] tem uma cadeia de herança de IList, ICollection, IEnumerable, IList <string>, ICollection <string >, IEnumerable <string>. Portanto, não tenho uma resposta melhor do que @mquander!
Scott Rippey
@ScottRippey Sim. A observação estranha que você notou é, na verdade, parte de um "hack" - e tem algumas implicações bastante estranhas em relação ao "tamanho fixo" e propriedades semelhantes (com algumas inconsistências dependendo de como você o lança). Existem alguns comentários bastante grandes sobre esse assunto no código-fonte .net. Desculpe por não ligar, mas se bem me lembro é bastante fácil de encontrar (dentro da classe de matriz). (E há também uma grande questão SO discutir as inconsistências .... em algum lugar ...> __>)
AnorZaken
@ScottRippey apenas FYI Eu encontrei esta resposta que tem a ver com o seu comentário: stackoverflow.com/a/4482567/2063755
David Klempfner
14

Eu encontrei os outros benchmarks que as pessoas fizeram aqui faltando, então aqui está a minha falha. Deixe-me saber se você encontrar algo errado com a minha metodologia.

/* 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

Você pode baixar o script LINQPad aqui .

Resultados: ToArray vs ToList desempenho

Ajustando o código acima, você descobrirá que:

  1. A diferença é menos significativa ao lidar com matrizes menores . Mais iterações, mas matrizes menores
  2. A diferença é menos significativa ao lidar com ints em vez de strings.
  3. Usar structs grandes em vez de strings geralmente leva muito mais tempo, mas na verdade não altera muito a proporção.

Isso concorda com as conclusões das respostas mais votadas:

  1. É improvável que você note uma diferença de desempenho, a menos que seu código esteja produzindo muitas vezes grandes listas de dados. (Havia apenas uma diferença de 200ms na criação de 1000 listas de 100K strings cada.)
  2. ToList() é executado de forma consistente com mais rapidez e seria uma escolha melhor se você não planeja manter os resultados por um longo tempo.

Atualizar

A @JonHanna apontou que, dependendo da implementação Select, é possível que uma ToList()ou ToArray()implementação preveja o tamanho da coleção resultante com antecedência. A substituição .Select(i => i)no código acima por Where(i => true) resultados muito semelhantes no momento e é mais provável que isso seja feito independentemente da implementação do .NET.

Benchmark usando Where em vez de Select

StriplingWarrior
fonte
No .NET Core, os dois casos devem ser melhores aqui do que no netfx, porque eles perceberão que o tamanho será 100000e os usarão para otimizar os dois ToList()e ToArray(), ToArray()sendo um pouco mais leves, porque não precisam da operação de redução necessária. caso contrário, qual é o único local ToList()tem a vantagem. O exemplo na pergunta ainda perderia, porque os Wheremeios que a previsão de tamanho não pode ser feita.
Jon Hanna
@ JonHanna: Obrigado pelo feedback rápido. Eu não sabia que o .NET Core estava fazendo essa otimização. Isso é legal. No meu código, .Select(i => i)poderia ser substituído por .Where(i => true)para corrigir isso.
StriplingWarrior
Sim, isso interromperia a otimização que o afetava no corefx. Pode ser interessante ter um tamanho que é uma potência de dois (o que deve dar ToArray()uma vantagem) e um que não é, como acima, e comparar os resultados.
Jon Hanna
@ JonHanna: Curiosamente, ToArray() ainda perde no melhor cenário. Com Math.Pow(2, 15)elementos, é (ToList: 700ms, ToArray: 900ms). A adição de mais um elemento o leva a (ToList: 925, ToArray: 1350). Gostaria de saber se ToArrayainda está copiando a matriz, mesmo quando já é o tamanho perfeito? Eles provavelmente pensaram que era uma ocorrência rara o suficiente para que não valesse a pena o condicional extra.
StriplingWarrior
Ele não copiava na correspondência exata de tamanho, mesmo antes de começarmos a otimizá-la no corefx, por isso é o caso em que ocorre mais interrupções.
Jon Hanna
12

Você deve basear sua decisão de escolher ToListou com ToArraybase no que é ideal a escolha do design. Se você deseja uma coleção que só pode ser iterada e acessada por índice, escolha ToArray. Se você quiser recursos adicionais para adicionar e remover da coleção mais tarde, sem muito aborrecimento, faça um ToList(não é realmente que você não possa adicionar a uma matriz, mas essa não é a ferramenta correta para isso normalmente).

Se o desempenho for importante, considere também o que seria mais rápido de operar. Realisticamente, você não telefonará ToListou ToArrayum milhão de vezes, mas poderá trabalhar na coleção obtida um milhão de vezes. Nesse aspecto []é melhor, pois List<>é []com alguma sobrecarga. Veja este tópico para obter algumas comparações de eficiência: Qual é mais eficiente: List <int> ou int []

Nos meus próprios testes, há um tempo atrás, eu encontrei ToArraymais rápido. E não tenho certeza de quão distorcidos os testes foram. A diferença de desempenho é tão insignificante, que pode ser notada apenas se você estiver executando essas consultas em um loop milhões de vezes.

nawfal
fonte
2
Sim - se o compilador souber que você está repetindo uma matriz (em vez de um IEnumerable <>), ele pode otimizar significativamente a iteração.
RobSiklos
12

Uma resposta muito tardia, mas acho que será útil para os googlers.

Ambos são péssimos quando criaram usando linq. Ambos implementam o mesmo código para redimensionar o buffer, se necessário . ToArrayusa internamente uma classe para converterIEnumerable<> em matriz, alocando uma matriz de 4 elementos. Se isso não for suficiente, ele dobrará o tamanho criando uma nova matriz, dobrando o tamanho da corrente e copiando a matriz atual para ela. No final, ele aloca uma nova matriz de contagem de seus itens. Se sua consulta retornar 129 elementos, o ToArray fará 6 alocações e operações de cópia de memória para criar uma matriz de 256 elementos e, em seguida, uma outra matriz de 129 para retornar. muito pela eficiência da memória.

A ToList faz a mesma coisa, mas ignora a última alocação, pois você pode adicionar itens no futuro. A lista não se importa se é criada a partir de uma consulta linq ou criada manualmente.

para criação A lista é melhor com memória, mas pior com a CPU, uma vez que a lista é uma solução genérica. Toda ação requer verificações de alcance adicionais às verificações internas de arrays do .net.

Portanto, se você repetir o conjunto de resultados muitas vezes, as matrizes serão boas, pois significam menos verificações de intervalo do que as listas, e os compiladores geralmente otimizam as matrizes para acesso seqüencial.

A alocação de inicialização da lista pode ser melhor se você especificar o parâmetro de capacidade ao criá-lo. Nesse caso, ele alocará a matriz apenas uma vez, supondo que você saiba o tamanho do resultado. ToListO linq não especifica uma sobrecarga para fornecê-lo, portanto, precisamos criar nosso método de extensão que cria uma lista com a capacidade especificada e depois usa List<>.AddRange.

Para finalizar esta resposta, tenho que escrever as seguintes frases

  1. No final, você pode usar um ToArray ou ToList, o desempenho não será tão diferente (consulte a resposta do @EMP).
  2. Você está usando c #. Se você precisar de desempenho, não se preocupe em escrever sobre código de alto desempenho, mas se preocupe em não escrever código de desempenho ruim.
  3. Sempre segure x64 para obter código de alto desempenho. O AFAIK, x64 JIT é baseado no compilador C ++ e faz algumas coisas engraçadas, como otimizações de recursão de cauda.
  4. Com o 4.5, você também pode aproveitar a otimização guiada por perfil e o JIT multinúcleo.
  5. Por fim, você pode usar o padrão async / waitit para processá-lo mais rapidamente.
Erdogan Kurtur
fonte
Ambos são péssimos? Você tem uma ideia alternativa que não exija alocação de memória redundante?
Nawfal # 22/13
No contexto da pergunta, sim, ambos são péssimos, mas por causa de alocações redundantes e nada mais. Para reduzir a alocação redundante, pode-se usar listas vinculadas à custa da memória e da velocidade da iteração. No final das contas, é isso que fazemos, fazemos compensações. Outra idéia é criar uma lista com capacidade para 200 (por exemplo) e carregar itens. Isso também reduzirá a redundância, mas as matrizes são sempre mais rápidas, portanto, isso é outra troca.
Erdogan Kurtur
Crie uma lista de 200 ? Isso pode evitar o redimensionamento, mas eu estava falando sobre a memória redundante usada. Você não pode evitar, porque não existe um pré-conhecimento sobre o tamanho. Você já pode especificar a capacidade no construtor de a List<T>, mas quando não o faz ou quando não pode, não pode ajudá-lo.
Nawfal #
2
os únicos dados redundantes na memória são o conteúdo da matriz, que é uma lista de ponteiros (neste caso). um milhão de ponteiros de 64 bits ocupa 8 MB de memória, o que não é nada comparado a um milhão de objetos apontados. 200 é apenas um número e tem a chance de reduzir o número de chamadas de redimensionamento no máximo 5 vezes. e sim, não podemos evitar. não temos melhores opções. Não tenho uma solução melhor, mas isso não significa que não tenho permissão para dizer onde está o problema.
Erdogan Kurtur
1
hmm no final, é onde você desenha a linha. Eu gosto da implementação atual. O tom de sua resposta me fez pensar que era crítica em vez de onde está o problema :)
Nawfal
7

Essa é uma pergunta antiga - mas para o benefício dos usuários que a encontram, há também uma alternativa de 'Memoizar' o Enumerável - que tem o efeito de armazenar em cache e interromper a enumeração múltipla de uma instrução Linq, que é o que ToArray () e ToList () são usados ​​muito, mesmo que os atributos de coleção da lista ou matriz nunca sejam usados.

Memoize está disponível na RX / System.Interactive lib e é explicado aqui: Mais LINQ com System.Interactive

( No blog de Bart De'Smet, que é uma leitura altamente recomendada se você estiver trabalhando muito com o Linq to Objects)

Frep D-Oronge
fonte
4

Uma opção é adicionar seu próprio método de extensão que retorna apenas uma leitura ICollection<T> . Isso pode ser melhor do que usar ToListou ToArrayquando você não deseja usar as propriedades de indexação de uma matriz / lista ou adicionar / remover de uma lista.

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();
    }
}

Testes unitários:

[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);
    }
}
Weston
fonte
Vale ressaltar que o contrato de coleta somente leitura estipula que o usuário do objeto não pode modificá-lo, mas o proprietário ainda poderá fazê-lo se mantiver uma referência a ele que ofereça uma interface mutável. Para interfaces que garantem que a estrutura subjacente nunca seja alterada, observe coleções imutáveis. Quanto ao porquê coleções imutáveis, somente leitura ou simples de leitura e gravação são melhores ou piores, é necessário um ponto de referência para comparação; não há resposta definitiva (caso contrário, não precisaríamos escolher).
tne
@ Nota: Eu faço o Tolist antes do AsReadOnly, portanto não há referências ao mutável subjacente.
Weston
Você está totalmente certo e essa foi provavelmente a melhor maneira de fazer as coisas antes que coleções imutáveis ​​chegassem ao BCL (vejo a primeira versão beta lançada um mês após a sua resposta).
tne
Existem coleções imutáveis ​​para segurança do encadeamento, em que os encadeamentos podem assumir que ele não será alterado e, caso isso ocorra, uma nova versão será criada, em vez de competir com os leitores e alterá-la enquanto eles a usam. Dessa forma, ninguém precisa adquirir uma trava.
doug65536
4

ToListAsync<T>() é preferível.

No Entity Framework 6, ambos os métodos chamam o mesmo método interno, mas ToArrayAsync<T>()chama list.ToArray()no final, que é implementado como

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

O mesmo ocorre ToArrayAsync<T>()com algumas despesas gerais, portanto ToListAsync<T>()é preferido.

Stephen Zeng
fonte
1
Essa é realmente a resposta que eu estava procurando, como a EF faz isso. Eu ficaria curioso para saber como é o EF Core.
Shimmy Weitzhandler
3

Pergunta antiga, mas novos questionadores o tempo todo.

De acordo com a fonte do System.Linq.Enumerable , ToListbasta retornar a new List(source), enquanto ToArrayusa a new Buffer<T>(source).ToArray()para retornar a T[].

Sobre a alocação de memória:

Enquanto estiver executando em um IEnumerable<T>único objeto, ToArrayaloque memória mais uma vez que ToList. Mas você não precisa se preocupar com isso na maioria dos casos, porque o GC fará a coleta de lixo quando necessário.

Sobre o tempo de execução eficiente:

Aqueles que estão questionando essa pergunta podem executar o código a seguir em sua própria máquina e você receberá sua resposta.

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);
}

Eu obtive estes resultados na minha máquina:

Grupo 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]

Grupo2:

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

Grupo3:

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

Grupo4:

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]

Grupo5:

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]

Devido ao limite do stackoverflow na quantidade de caracteres da resposta, as listas de amostra do Grupo2 e do Grupo3 são omitidas.

Como você pode ver, não é realmente importante usar ToListou ToArryna maioria dos casos.

Ao processar IEnumerable<T>objetos calculados em tempo de execução , se a carga trazida pelo cálculo for pesada que as operações de alocação e cópia de memória de ToListe ToArray, a disparidade será insignificante ( C.ToList vs C.ToArrayeS.ToList vs S.ToArray ).

A diferença pode ser observada apenas em IEnumerable<T>objetos calculados sem tempo de execução ( C1.ToList vs C1.ToArraye S1.ToList vs S1.ToArray). Mas a diferença absoluta (<60ms) ainda é aceitável em um milhão de objetos pequenosIEnumerable<T> . De fato, a diferença é decidida pela implementação Enumerator<T>de IEnumerable<T>. Portanto, se seu programa é realmente muito sensível a isso, você precisa criar um perfil, perfil, perfil ! Por fim, você provavelmente descobrirá que o gargalo não está ativado ToListouToArray , mas sim os detalhes dos enumeradores.

E, o resultado C2.ToList vs C2.ToArraye S2.ToList vs S2.ToArraymostra que você realmente não precisa se preocupar ToListou ToArraycalculado sem tempo de execuçãoICollection<T> objetos .

Obviamente, são apenas resultados na minha máquina, o tempo real gasto com essas operações em máquinas diferentes não será o mesmo, você pode descobrir na sua máquina usando o código acima.

A única razão pela qual você precisa fazer uma escolha é que você tem necessidades específicas List<T>ou T[], conforme descrito pela resposta de @Jeppe Stig Nielsen .

qaqz111
fonte
1

Para qualquer pessoa interessada em usar esse resultado em outro Linq-to-sql, como

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

o SQL gerado será o mesmo, independentemente de você ter usado uma Lista ou Matriz para o myListOrArray. Agora eu sei que alguns podem perguntar por que até enumerar antes dessa declaração, mas há uma diferença entre o SQL gerado a partir de um IQueryable vs (List ou Array).

Gary
fonte