4 maneiras diferentes de criar uma matriz

Imagem de capa 4 maneiras diferentes de criar uma matriz

4 maneiras diferentes de criar uma matriz

Neste post do blog, mostrarei 4 maneiras diferentes de criar uma matriz e como elas diferem umas das outras.

Novo

A primeira é bem óbvia, usamos o operador new para criar uma nova matriz:

var myArray = new  int[100];

Criamos uma matriz com 100 elementos. Importante aqui é que "nós" temos que fazer o trabalho pesado de criar a matriz, mas por outro lado não somos responsáveis por liberar a memória. O querido Garbage Collector (GC) faz isso por nós.

ConjuntoDeMatrizes

Você pode imaginar um conjunto de matrizes como um conjunto de carros. Você compartilhará a matriz com outras pessoas. Quando você precisa de uma matriz, você pode "alugá-lo" do conjunto, mas você tem que devolver depois de terminar. Essa é a grande diferença para criar uma matriz com new. A responsabilidade de limpar essa memória passa para você.

var myArray = ArrayPool<int>.Shared.Rent(100);
// Do something
ArrayPool<int>.Shared.Return(myArray);

Certifique-se de retornar sua matriz, caso contrário o conjunto de matrizes pode "morrer de fome" e precisa criar novos elementos no conjunto, o que pode afetar seu desempenho. Também o que é especial aqui é que usamos a instância compartilhada do conjunto de matrizes. Palavra especial para alugar. Seu único parâmetro é chamado Comprimento mínimo, o que significa que sua matriz tem a garantia de ter o Comprimento mínimo, mas também pode ser maior que isso. Se quiser saber mais, confira este post.

CG.AlocarMatriz (GC.AllocateArray)

CG. Alocar matriz é outra maneira de criar uma matriz. Ele tem dois parâmetros: o primeiro é o comprimento mais um sinalizador booleano descrevendo se a matriz deve ou não ser fixado. Matrizes fixas significam que o GC não deve se mover pelo bloco de memória associado à matriz. Isso é útil se você precisar trabalhar com recursos não gerenciados, caso contrário, não há muitos motivos para fazê-lo.

A propósito, você pode conseguir o mesmo com matrizes "normais" e a palavra-chave fixa. Aqui está como você usaria o GC. Alocar matriz:

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

CG.AlocarMatrizNãoInicializada (GC.AllocateUninitializedArray)

Essa chamada de API faz quase o mesmo que GC.AllocateArray com um pequeno toque. Normalmente em .NET matrizes (array) são inicializados com o valor padrão, então se você tiver algo como: var myArray = new int[2]; Em seguida, _myArray[0] == 0; - e myArray[1] == 0. Em termos de desempenho, isso pode reduzir um pouco mais do que a versão inicializada. A propósito, você pode conseguir o mesmo com o Ignorar atributo local permite

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

Observações do GC

Os dois últimos métodos mostrados acima são destinados à micro-otimização. Esteja ciente de que esses métodos também têm algumas restrições, por exemplo, você não pode usar _GC.AllocateArray e GC.AllocateUninitializedArray não são permitidos com tipos de referência. Pode-se verificar com RuntimeHelpers.IsReferenceOrContainsReferences() se um tipo é elegível ou não.

Comparação

Vamos verificar como esses diferentes tipos estão se saindo com diferentes tamanhos de matrizes. Isenção de responsabilidade: Use new[] em quase todos os casos. As outras opções são destinadas a otimizações de hot path e não para uso geral. Portanto, avalie seu caso de uso e decida depois.

[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:

|              Method | 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 arrays menores não há uso real das APIs alternativas. Novamente, avalie seu caso primeiro e esteja ciente das consequências e das "novas" responsabilidades.

Recursos

  • O repositório de comparação de desempenho pode ser encontrado aqui.