🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Olá a todos! Este artigo é uma coletânea de Melhores Práticas que eu e meus colegas aplicamos há muito tempo ao trabalhar em diversos projetos.

Informações sobre a máquina onde os cálculos foram realizados:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 núcleos lógicos e 4 físicos
.NET Core SDK=3.0.100
[Host]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
Core: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
[Host]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
Core: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT

Job=Core Runtime=Core

ToList vs ToArray e Ciclos


Eu planejava preparar essas informações com o lançamento do .NET Core 3.0, mas fui antecipado; não quero roubar o crédito de ninguém e copiar informações de terceiros, então apenas mencionarei um link para um bom artigo que detalha a comparação.

De minha parte, gostaria apenas de apresentar minhas medições e resultados, adicionei ciclos reversos para os fãs de ‘estilo C++’ de escrever laços.

Código:

public class Bench
    {
        private List<int> _list;
        private int[] _array;

        [Params(100000, 10000000)] public int N;

        [GlobalSetup]
        public void Setup()
        {
            const int MIN = 1;
            const int MAX = 10;
            Random random = new Random();
            _list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
            _array = _list.ToArray();
        }

        [Benchmark]
        public int ForList()
        {
            int total = 0;
            for (int i = 0; i < _list.Count; i++)
            {
                total += _list[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForListFromEnd()
        {
            int total = 0;
            for (int i = _list.Count-1; i > 0; i--)
            {
                total += _list[i];
            }

            return total;
        }

        [Benchmark]
        public int ForeachList()
        {
            int total = 0;
            foreach (int i in _list)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForeachArray()
        {
            int total = 0;
            foreach (int i in _array)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForArray()
        {
            int total = 0;
            for (int i = 0; i < _array.Length; i++)
            {
                total += _array[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForArrayFromEnd()
        {
            int total = 0;
            for (int i = _array.Length-1; i > 0; i--)
            {
                total += _array[i];
            }

            return total;
        }
    }

A velocidade de operação no .NET Core 2.2 e 3.0 é quase idêntica. Aqui está o que consegui no .NET Core 3.0:

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Podemos concluir que o processamento cíclico de uma coleção do tipo Array é mais rápido, devido às suas otimizações internas e ao dimensionamento explícito da coleção. Também é importante lembrar que a coleção do tipo List tem suas vantagens e você deve usar a coleção adequada dependendo dos cálculos necessários. Mesmo que você esteja escrevendo a lógica de funcionamento com ciclos, não se esqueça de que isso é um loop padrão e também está sujeito a possíveis otimizações de ciclos. Há bastante tempo, saiu um artigo no Habr: https://habr.com/ru/post/124910/. Ele ainda é relevante e recomendado para leitura.

Throw

Há um ano, trabalhei em uma empresa em um projeto legado, onde era normal processar a validação de campos através de uma construção try-catch-throw. Já na época, percebia que essa lógica de negócios não era saudável, portanto, sempre que possível, tentava evitar tal construção. Mas vamos entender por que a abordagem de tratar erros dessa forma é ruim. Escrevi um pequeno código para comparar duas abordagens e fiz alguns "benchmarks" para cada opção.

Código:

        public bool ContainsHash()
        {
            bool result = false;
            foreach (var file in _files)
            {
                var extension = Path.GetExtension(file);
                if (_hash.Contains(extension))
                    result = true;
            }

            return result;
        }

        public bool ContainsHashTryCatch()
        {
            bool result = false;
            try
            {
                foreach (var file in _files)
                {
                    var extension = Path.GetExtension(file);
                    if (_hash.Contains(extension))
                        result = true;
                }
                
                if(!result) 
                    throw new Exception("false");
            }
            catch (Exception e)
            {
                result = false;
            }

            return result;
        }

Os resultados no .NET Core 3.0 e Core 2.2 são semelhantes (.NET Core 3.0):

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Try catch torna o código mais difícil de entender e aumenta o tempo de execução do seu programa. Mas se você precisa dessa construção, evite adicionar aquelas linhas de código que não requerem tratamento de erros — isso facilitará a compreensão do código. Na verdade, o que sobrecarrega o sistema não é tanto o tratamento de exceções, mas o lançamento real de erros por meio da construção throw new Exception.

Lançar exceções é mais lento do que qualquer classe que reúna o erro no formato necessário. Se você está processando um formulário ou alguns dados e sabe exatamente qual erro deve ocorrer, por que não tratá-los?

Não é aconselhável usar a construção throw new Exception() se essa situação não for excepcional. O tratamento e a geração de exceções são extremamente caros!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Em meus 5 anos de experiência na plataforma .NET, encontrei muitos projetos que utilizavam comparações de strings. Também vi o seguinte cenário: havia uma solução Enterprise com vários projetos, cada um dos quais realizava comparações de strings de maneira diferente. Mas o que realmente devemos usar e como podemos unificá-lo? No livro CLR via C#, de Richter, encontrei informações de que o método ToUpperInvariant() é mais rápido do que o ToLowerInvariant().

Trecho do livro:

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Claro que eu não acreditei e decidi fazer alguns testes na época, ainda no .NET Framework, e o resultado me surpreendeu — mais de 15% de aumento de desempenho. No dia seguinte, ao chegar ao trabalho, mostrei os dados das medições ao meu chefe e lhes dei acesso ao código-fonte. Após isso, 2 dos 14 projetos foram alterados para novas medições, e considerando que esses dois projetos existiam para processar enormes tabelas do Excel, o resultado foi muito significativo para o produto.

Também apresento medições para diferentes versões do .NET Core, para que cada um de vocês possa escolher a solução mais otimizada. E só quero acrescentar que na empresa onde trabalho, utilizamos o ToUpper() para comparações de strings.

Código:

public const string defaultString =  "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";

        [Benchmark]
        public bool ToLower()
        {
            return defaultString.ToLower() == defaultString.ToLower();
        }

        [Benchmark]
        public bool ToLowerInvariant()
        {
            return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
        }

        [Benchmark]
        public bool ToUpper()
        {
            return defaultString.ToUpper() == defaultString.ToUpper();
        }

        [Benchmark]
        public bool ToUpperInvariant()
        {
            return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
        }

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

No .NET Core 3.0, o ganho para cada um desses métodos é de aproximadamente x2 e equilibra as implementações entre si.

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Compilação em Camadas

No meu artigo anterior, descrevi essa funcionalidade brevemente, e gostaria de corrigir e complementar minhas palavras. A compilação em vários níveis acelera o tempo de inicialização da sua solução, mas você sacrifica o fato de que partes do seu código serão compiladas em uma versão mais otimizada em segundo plano, o que pode resultar em pequenas sobrecargas. Com a chegada do .NET Core 3.0, o tempo de compilação de projetos com a compilação em vários níveis foi reduzido e os bugs relacionados a essa tecnologia foram corrigidos. Antes, essa tecnologia resultava em erros nas primeiras requisições no ASP.NET Core e em travamentos durante a primeira compilação no modo de compilação em vários níveis. Atualmente, no .NET Core 3.0, ela está habilitada por padrão, mas você pode desativá-la se desejar. Se você ocupa uma posição de líder de equipe, sênior, intermediário ou é chefe de departamento, deve entender que uma rápida evolução do projeto aumenta o valor da equipe, e essa tecnologia permitirá que você economize tempo tanto dos desenvolvedores quanto do próprio tempo de desenvolvimento do projeto.

.NET subir de nível

Atualize a versão do seu .NET Framework / .NET Core. Muitas vezes, cada nova versão oferece um ganho adicional de desempenho e adiciona novos recursos.

Mas quais são exatamente as vantagens? Vamos analisar algumas delas:

  • No .NET Core 3.0, surgiram as imagens R2R, que permitirão reduzir o tempo de inicialização de aplicativos .NET Core.
  • A partir da versão 2.2, a Compilação em Nível foi introduzida, permitindo que os programadores gastem menos tempo inicializando o projeto.
  • Suporte aos novos padrões do .NET Standard.
  • Suporte à nova versão da linguagem de programação.
  • Otimização, a cada nova versão a otimização das bibliotecas básicas Collection/Struct/Stream/String/Regex e muito mais melhora. Se você está migrando do .NET Framework para o .NET Core, terá um grande ganho de desempenho desde o início. Para exemplo, anexo um link para algumas das otimizações que foram adicionadas no .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

🥇UDP Flood do Google ou como não privar todos do Youtube | ProHoster

Conclusão

Ao escrever código, é importante prestar atenção a diferentes aspectos do seu projeto e utilizar as funções da sua linguagem de programação e plataforma para alcançar o melhor resultado. Fico feliz se você compartilhar seus conhecimentos relacionados à otimização no .NET.

Link para GitHub

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster