Este artigo é um artigo espelhado de tradução automática, por favor clique aqui para ir para o artigo original.

Vista: 6846|Resposta: 2

[Fonte] [Virar]. Otimização de Desempenho do .NET - Collections. O Pooled é recomendado

[Copiar link]
Postado em 29-05-2022 13:45:22 | | | |
Breve introdução

Otimização de desempenho é como garantir que o mesmo número de requisições seja processado com menos recursos, que geralmente são CPU ou memória, e claro, controladores de IOs do sistema operacional, tráfego de rede, uso de disco, etc. Mas, na maioria das vezes, estamos reduzindo o uso de CPU e memória.
O conteúdo compartilhado antes tem algumas limitações, é difícil transformar diretamente, hoje quero compartilhar com vocês um método simples: basta substituir alguns tipos de coleção, para alcançar o efeito de melhorar o desempenho e reduzir o consumo de memória.
Hoje quero compartilhar com vocês uma biblioteca de turma, estaA biblioteca de classes se chama Collections.Pooled, Como pode ser visto pelo nome, é por meio de memória em pool para alcançar o objetivo de reduzir a pegada de memória e o GC, e veremos diretamente como é seu desempenho, além de levar você para ver o código-fonte, por que ele traz essas melhorias de desempenho.

Coleções. Agrupadas

Link do projeto:O login do hiperlink está visível.

A biblioteca é baseada em classes do System.Collections.Generic, que foram modificadas para aproveitar as novas bibliotecas de classes System.Span <T>e System.Buffers.ArrayPool <T>com o objetivo de reduzir a alocação de memória, melhorar o desempenho e permitir maior interoperabilidade com APIs modernas.
O Collections.Pooled suporta .NET Standnd 2.0 (.NET Framework 4.6.1+), assim como suporte para . NET Core 2.1+. Um conjunto extenso de testes unitários e benchmarks foi portado do CoreFX.

Número total de testes: 27.501. Via com: 27501. Fracasso: 0. Pular: 0.
O teste foi bem-sucedido.
Tempo de execução do teste: 9,9019 segundos

Como usar

Você pode facilmente instalar essa biblioteca pelo Nuget, NuGet Version.

Na biblioteca Collections.Pooled, ele implementa versões em pool para os tipos de coleções que comumente usamos, como mostrado na comparação com os tipos nativos .NET.

. .NET nativoColeções. Agrupadascomentário
Lista<T>PooledList<T>Classes genéricas de coleção
Dicionário<TKey, TValue>DicionárioPooled<TKey, TValue>Classe genérica de dicionário
HashSet<T>PooledSet<T>Classes genéricas de coleção de hashes
Pilha<T>Pilha<T>Stacks genéricos
Fila<T>PooledQueue<T>Coorte genérica

Ao usar, basta adicionar o correspondente . .NET com a versão Collections.Pooled, conforme mostrado no código abaixo:

No entanto, precisamos notar que o tipo Pooled implementa a interface IDispose, que retorna a memória usada ao pool através do método Dispose(), então precisamos chamar seu método Dispose() após usar o objeto de coleção Pooled. Ou você pode usar a palavra-chave using var diretamente.

Nota: Use o objeto collection dentro de Collections.PooledÉ melhor precisar liberá-lo manualmente, mas não importa se você não liberar, o GC eventualmente vai reciclar, mas não pode ser devolvido ao pool, e não vai conseguir o efeito de economizar memória.
Como reutiliza espaço de memória, ao retornar espaço de memória ao pool, ele precisa processar os elementos da coleção, e fornece uma enumeração chamada ClearMode para uso, definida da seguinte forma:




Por padrão, você pode usar o valor padrão Auto, e se houver requisitos especiais de desempenho, pode usar Never após conhecer os riscos.
Para tipos de referência e tipos de valor que contêm tipos de referência, devemos esvaziar a referência do array ao retornar o espaço de memória ao pool; se não for limpo, o GC não poderá liberar essa parte do espaço de memória (porque a referência do elemento sempre foi mantida pelo pool); se for um tipo de valor puro, então não pode ser esvaziado; neste artigo descrevo a diferença de armazenamento entre tipos de referência e arrays de struct (tipo de valor), tipos de valor puro não possuem reciclagem de cabeçalhos de objeto e não exigem intervenção do GC.


. Otimização de Desempenho .NET - Use classes alternativas de strutt:O login do hiperlink está visível.

Comparação de desempenho

Eu não fiz o Benchmark sozinho, e os resultados de pontuação dos projetos open source que usei diretamente foram 0 para o uso de memória de muitos projetos, o que aconteceu porque a memória em pool usada não tinha alocação extra.

PooledList<T>

Passe pelos 2048 elementos adicionados ao conjunto no Benchmark, . .NET native <T>List requer 110us (de acordo com os resultados reais do benchmark, os milissegundos na figura devem ser um erro administrativo) e 263KB de memória, enquanto PooledList <T>precisa apenas de 36us e 0KB de memória.




DicionárioPooled<TKey, TValue>

Adicione 10_0000 elementos ao dicionário em um loop no Benchmark, . .NET Native Dictionary<TKey, TValue> requer 11ms e 13MB de memória, enquanto PooledDictionary<TKey, TValue> requer apenas 7ms e 0MB de memória.




PooledSet<T>

Faça o loop pela coleção de hashes no Benchmark e adicione 10_0000 elementos, . O HashSet nativo <T>.NET requer 5348ms e 2MB, enquanto o PooledSet <T>requer apenas 4723ms e 0MB de memória.




PooledStack<T>

Faça um loop pela pilha no Benchmark para adicionar 10_0000 elementos, . O PooledStack nativo .<T>NET requer 1079ms e 2MB, enquanto o PooledStack <T>requer apenas 633ms e 0MB de memória.




PooledQueue<T>

Faça o loop pelos loops no Benchmark para adicionar 10_0000 elementos à fila, . O <T>PooledQueue nativo .NET requer 681ms e 1MB, enquanto o PooledQueue <T>requer apenas 408ms e 0MB de memória.




A cena não é liberada manualmente

Além disso, mencionamos acima que o tipo de coleta agrupada precisa ser liberado, mas não importa se não for liberado, porque o GC vai reciclar.


Os resultados do Benchmark são os seguintes:



A conclusão pode ser tirada dos resultados do Benchmark acima.

Liberar a coleção de tipos Pooled a tempo mal aciona o GC e aloca memória; no gráfico acima, ele aloca apenas 56 bytes de memória.
Mesmo que a coleção de tipos Pooled não seja liberada, porque ela aloca memória do pool, ela ainda reutilizará memória durante a operação de expansão ReSize e pulará a etapa de inicialização da alocação de memória do GC, que é relativamente rápida.
O mais lento é usar o tipo normal de coleta, cada operação de expansão ReSize precisa solicitar novo espaço de memória, e o GC também precisa recuperar o espaço de memória anterior.


Análise de princípio

Se você leu meu post anterior no blog, deve definir o tamanho inicial para os tipos de coleção e analisar o princípio de implementação do Dicionário C#. Você pode saber que desenvolvedores BCL .NET usam as estruturas de dados subjacentes desses tipos básicos de coleção como arrays para acesso aleatório de alto desempenho, vamos pegar o List <T>como exemplo.

Crie um novo array para armazenar os elementos adicionados.
Se não houver espaço suficiente no array, a operação de expansão é acionada para solicitar o dobro do tamanho do espaço.
O código construtor é o seguinte, e você pode ver que é um array genérico criado diretamente:


Então, se você quiser agrupar memória, só precisa mudar o local onde a nova aplicação de palavra-chave é usada na biblioteca de classes para usar a aplicação em pool. Aqui eu compartilho com você. NET BCL é um tipo chamado ArrayPool, que fornece um pool de recursos de array de instâncias genéricas reutilizáveis, que podem ser usadas para reduzir a pressão sobre o GC e melhorar o desempenho em caso de criação e destruição frequente de arrays.

A camada subjacente do nosso tipo Pooled é usar o ArrayPool para compartilhar pools de recursos, e pelo seu construtor, podemos ver que ele usa o ArrayPool por padrão<T>. Compartilhado para atribuir objetos de array, e claro que você também pode criar seu próprio ArrayPool para usá-lo.


Além disso, ao realizar uma operação de ajuste de capacidade (expansão), o array antigo é devolvido ao pool de threads, e o novo array também é adquirido do pool.

Além disso, o autor usa o Span para otimizar APIs como Add e Insert, proporcionando melhor desempenho em acesso aleatório. Além disso, a API da série TryXXX foi adicionada, então você pode usá-la de forma mais conveniente. Por exemplo, a <T>classe List <T>tem até 170 modificações em comparação com a PooledList.



resumo

No nosso uso online real, podemos substituir o tipo de coleção nativo pelo tipo de coleção fornecido pelo Pooled, o que é muito útil para reduzir o uso de memória e a latência do P95.
Além disso, mesmo que você esqueça de lançar, o desempenho não será muito pior do que usando o tipo de coleção nativo. Claro, o melhor hábito é liberá-lo a tempo.


Original:O login do hiperlink está visível.




Anterior:O RecyclableMemoryStream oferece streaming de .NET de alto desempenho
Próximo:[Combate prático] O servidor constrói o LibreSpeed para testar a velocidade da rede
Postado em 29-05-2022 17:12:36 |
Aprenda
Publicado em 20-6-2022 09:09:22 |
Aprenda a mistura
Disclaimer:
Todo software, material de programação ou artigos publicados pela Code Farmer Network são apenas para fins de aprendizado e pesquisa; O conteúdo acima não deve ser usado para fins comerciais ou ilegais, caso contrário, os usuários terão todas as consequências. As informações deste site vêm da Internet, e disputas de direitos autorais não têm nada a ver com este site. Você deve deletar completamente o conteúdo acima do seu computador em até 24 horas após o download. Se você gosta do programa, por favor, apoie um software genuíno, compre o registro e obtenha serviços genuínos melhores. Se houver qualquer infração, por favor, entre em contato conosco por e-mail.

Mail To:help@itsvse.com