4 maneiras diferentes de criar um Array em C#

Imagem de capa 4 maneiras diferentes de criar um Array em C#

Neste artigo vou mostrar 4 maneiras diferentes para criar um array em C# e como essas maneiras se diferem uma das outras

Utilizando "new"

A primeira é bem óbvia, nós usamos o operador new para criar um novo array:

var meuArray = new  int[100];

Nós criamos um array com 100 elementos. O importante aqui é que "nós" tivemos que fazer o trabalho difícil de criar o array, mas por outro lado, nós não somos responsáveis por liberar a memória. O querido Garbage Collector (GC) faz isso por nós.

ArrayPool

Você pode imaginar um ArrayPool como uma carona de carro. Você vai compartilhar o array com outros. Quando você precisar de um array, você pode "aluga-lo" temporariamente para utilização, mas você tem que devolve-lo após o trabalho. Essa é a grande diferença entre criar um novo array com new. A responsabilidade de limpar essa memória recai sobre você.

var meuArray = ArrayPool<int>.Shared.Rent(100);
// Faça seu trabalho
ArrayPool<int>.Shared.Return(meuArray);

Mas tenha certeza que você vai devolver o array, senão o ArrayPool pode ficar totalmente ocupado, e por não ter mais recursos para emprestar ele terá que criar novos elementos no seu estoque, o que pode degradar sua performance. Outra coisa especial aqui é, que nós usamos a instância Shared do ArrayPool e o método Rent. Seu único parâmetro é chamado minimumLenght, que significa que seu array terá garantidamente o tamanho mínimo, mas pode também ser maior que o solicitado. Se você quiser saber mais sobre isso, leia este artigo.

GC.AllocateArray

GC.AllocateArray é outra maneira de criar um array. Isto tem dois parâmetros: O Primeiro é o lenght mais um booleano para indicar se o array deve ser pinned. Arrays marcados como Pinned significam que o GC não deve mover pela memória os blocos associados com o array. Isto é muito útil se você trabalha com recursos não gerenciados, de outra forma, não existem muitas razões para utiliza-lo. De qualquer forma, você pode chegar ao mesmo resultado utilizando arrays "normais" com o termo fixed. Aqui está como você poderia utilizar o GC.AllocateArray:

var meuArray = GC.AllocateArray<int>(100);

GC.AllocateUninitializedArray

Esta maneira de criar um array através do AllocateUninitializedArray faz praticamente a mesma coisa que o GC.AllocateArray<int> com um pequeno toque. Normalmente no .NET os arrays são inicializados com o valor padrão, então, se você tem algo como: var meuArray = new int[2]; Então meuArray[0] == 0; e meuArray[1] == 0. Em termos de performance isso pode ser um pouco mais rápido do que a versão inicializada. De qualquer forma, você pode chegar ao mesmo resultado utilizando SkipLocalsInitAttribute. Aqui está como você poderia utilizar o GC.AllocateUninitializedArray:

var myArray = GC.AllocateUninitializedArray<int>(100);

Comparação

Vamos verificar como estas diferentes maneiras de criar array se comportam com tamanhos diferentes.
Aviso: Utilize new[] na maioria dos casos. As outras opções são designadas para otimizações profundas e não para uso geral. Então teste seu caso de uso e decida mais tarde.

[MemoryDiagnoser] 
public class ArrayBenchmark 
{ 
    [Params(10, 100, 1_000, 10_000, 100_000, 1_000_000)] 
    public int ArraySize { get; set; } 

    [Benchmark(Baseline = true)] 
    public int[] NewArray() => new  int[ArraySize]; 

    [Benchmark] 
    public int[] ArrayPoolRent() => ArrayPool<int>.Shared.Rent(ArraySize); 

    [Benchmark] 
    public int[] GCZeroInitialized() => GC.AllocateArray<int>(ArraySize); 

    [Benchmark] 
    public int[] GCZeroUninitialized() => GC.AllocateUninitializedArray<int>(ArraySize); 
}

Resultados

|              Metodo | ArraySize |             Mean |          Error |         StdDev |           Median | Ratio | RatioSD |    Gen 0 |    Gen 1 |    Gen 2 |   Allocated |
|-------------------- |---------- |-----------------:|---------------:|---------------:|-----------------:|------:|--------:|---------:|---------:|---------:|------------:|
|            NewArray |        10 |         4.674 ns |      0.2270 ns |      0.6175 ns |         4.500 ns |  1.00 |    0.00 |   0.0153 |        - |        - |        64 B |
|       ArrayPoolRent |        10 |        16.858 ns |      0.7670 ns |      2.2008 ns |        16.246 ns |  3.70 |    0.56 |   0.0210 |        - |        - |        88 B |
|   GCZeroInitialized |        10 |        32.604 ns |      0.6481 ns |      0.5412 ns |        32.407 ns |  6.83 |    0.75 |   0.0153 |        - |        - |        64 B |
| GCZeroUninitialized |        10 |         5.170 ns |      0.3419 ns |      0.9588 ns |         4.884 ns |  1.12 |    0.27 |   0.0153 |        - |        - |        64 B |
|                     |           |                  |                |                |                  |       |         |          |          |          |             |
|            NewArray |       100 |        18.643 ns |      0.4770 ns |      1.3057 ns |        18.034 ns |  1.00 |    0.00 |   0.1014 |        - |        - |       424 B |
|       ArrayPoolRent |       100 |        33.094 ns |      0.8942 ns |      2.4628 ns |        32.237 ns |  1.79 |    0.18 |   0.1281 |        - |        - |       536 B |
|   GCZeroInitialized |       100 |        47.771 ns |      1.1578 ns |      3.3033 ns |        47.263 ns |  2.59 |    0.25 |   0.1013 |        - |        - |       424 B |
| GCZeroUninitialized |       100 |        18.287 ns |      0.4325 ns |      0.3611 ns |        18.164 ns |  0.98 |    0.08 |   0.1014 |        - |        - |       424 B |
|                     |           |                  |                |                |                  |       |         |          |          |          |             |
|            NewArray |      1000 |       156.640 ns |      2.9920 ns |      2.7987 ns |       157.671 ns |  1.00 |    0.00 |   0.9613 |        - |        - |     4,024 B |
|       ArrayPoolRent |      1000 |       116.015 ns |      0.8502 ns |      0.7953 ns |       115.891 ns |  0.74 |    0.01 |   0.9813 |        - |        - |     4,120 B |
|   GCZeroInitialized |      1000 |       186.634 ns |      3.8069 ns |      8.7471 ns |       185.534 ns |  1.24 |    0.05 |   0.9613 |        - |        - |     4,024 B |
| GCZeroUninitialized |      1000 |       111.039 ns |      2.3022 ns |      3.9712 ns |       110.275 ns |  0.71 |    0.03 |   0.9587 |        - |        - |     4,024 B |
|                     |           |                  |                |                |                  |       |         |          |          |          |             |
|            NewArray |     10000 |     1,450.991 ns |     28.9030 ns |     59.0412 ns |     1,454.230 ns |  1.00 |    0.00 |   9.5234 |   0.0019 |        - |    40,024 B |
|       ArrayPoolRent |     10000 |       678.110 ns |     13.5437 ns |     16.1228 ns |       681.002 ns |  0.47 |    0.02 |  15.6240 |   0.0010 |        - |    65,560 B |
|   GCZeroInitialized |     10000 |     1,352.078 ns |     20.4704 ns |     18.1465 ns |     1,349.767 ns |  0.93 |    0.05 |   9.5234 |   0.0019 |        - |    40,024 B |
| GCZeroUninitialized |     10000 |       419.071 ns |      8.2441 ns |     10.1245 ns |       417.218 ns |  0.29 |    0.01 |   9.5234 |   0.0005 |        - |    40,024 B |
|                     |           |                  |                |                |                  |       |         |          |          |          |             |
|            NewArray |    100000 |    22,770.844 ns |    531.6404 ns |  1,473.1730 ns |    22,217.963 ns |  1.00 |    0.00 | 124.9695 | 124.9695 | 124.9695 |   400,066 B |
|       ArrayPoolRent |    100000 |    18,859.646 ns |    451.2290 ns |  1,287.3816 ns |    18,214.809 ns |  0.83 |    0.07 | 166.6565 | 166.6565 | 166.6565 |   524,317 B |
|   GCZeroInitialized |    100000 |    22,818.945 ns |    456.0302 ns |  1,293.6817 ns |    22,739.250 ns |  1.01 |    0.08 | 124.9695 | 124.9695 | 124.9695 |   400,066 B |
| GCZeroUninitialized |    100000 |    13,473.716 ns |    436.1365 ns |  1,265.3112 ns |    13,172.961 ns |  0.60 |    0.07 | 124.9847 | 124.9847 | 124.9847 |   400,025 B |
|                     |           |                  |                |                |                  |       |         |          |          |          |             |
|            NewArray |   1000000 | 1,108,601.149 ns | 22,592.9715 ns | 18,866.1545 ns | 1,114,450.342 ns |  1.00 |    0.00 | 139.6484 | 139.4043 | 139.4043 | 4,000,068 B |
|       ArrayPoolRent |   1000000 |   219,394.967 ns | 19,411.7449 ns | 57,235.9678 ns |   231,375.549 ns |  0.22 |    0.02 |  23.1934 |  23.1934 |  23.1934 | 4,194,329 B |
|   GCZeroInitialized |   1000000 | 1,146,780.574 ns | 14,884.3974 ns | 13,922.8745 ns | 1,144,215.051 ns |  1.03 |    0.02 | 140.8691 | 140.6250 | 140.6250 | 4,000,070 B |
| GCZeroUninitialized |   1000000 |   190,029.061 ns | 16,728.2807 ns | 49,323.7129 ns |   200,659.369 ns |  0.15 |    0.05 |  22.7051 |  22.7051 |  22.7051 | 4,000,023 B |

Como você pode ver, o ArrayPool usa um pouco mais de espaço do que o solicitado. Também para pequenos arrays, não há razão real para aplicar as alternativas abordadas. Novamente, por favor teste seu caso de uso primeiro e tenha cuidado com as consequências e as "novas" responsabilidades.

Recursos

  • O Repositório da comparação pode ser encontrado aqui