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.