Otimização de desempenho final do .NET8 CHRL

Prefácio

O .NET8 foi otimizado ainda mais com base no .NET7, como a tecnologia de otimização CHRL (nome completo: CORINFO_HELP_RNGCHKFAIL). CORINFO_HELP_RNGCHKFAIL é uma verificação de limite. No .NET7 foi parcialmente otimizado, mas no .NET8 continua a otimizar, semelhante a manual Inteligente, o .NET8 está ciente de certos problemas de desempenho e pode otimizá-los. Vamos dar uma olhada neste artigo

Endereço original: .NET8 Ultimate Performance Optimization CHRL

Visão geral

O JIT verificará os limites do intervalo de matrizes e strings. Por exemplo, se o índice da matriz está dentro do intervalo de comprimento da matriz e não pode excedê-lo. Portanto, o JIT gerará etapas de verificação de limites.

public class Tests
{
    private byte[] _array = new byte[8];
    private int _index = 4;

    public void Get() => Get(_array, _index);

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static byte Get(byte[] array, int index) => array[index];
}

O ASM da função Get .NET7 é o seguinte:

; Tests.Get(Byte[], Int32)
       sub       rsp,28
       cmp       edx,[rcx+8]
       jae       short M01_L00
       mov       eax,edx
       movzx     eax,byte ptr [rcx+rax+10]
       add       rsp,28
       ret
M01_L00:
       call      CORINFO_HELP_RNGCHKFAIL
       int       3

A instrução cmp compara o comprimento do array deslocado em 8 posições do MT do array (tabela de métodos) com o índice do array atual.Se os dois índices forem maiores que (o último) ou iguais (jae) ao comprimento do array (o primeiro). Ele irá pular para CORINFO_HELP_RNGCHKFAIL para verificação de limite, o que pode causar uma exceção IndexOutOfRangeException que excede o intervalo do índice. Mas na verdade, o acesso a este código requer apenas dois movs, um é o índice do array e o outro é (MT (tabela de métodos) + 0x10 + índice) e retorna o valor. Portanto, há uma otimização claramente visível aqui.
O .NET8 aprendeu alguma otimização inteligente dos limites do intervalo. Em outras palavras, a verificação dos limites não é necessária em alguns lugares, portanto, a verificação dos limites pode ser otimizada para melhorar o desempenho do código. Exemplo abaixo:

 private readonly int[] _array = new int[7];
   public int GetBucket() => GetBucket(_array, 42);
   private static int GetBucket(int[] buckets, int hashcode) =>
   buckets[(uint)hashcode % buckets.Length];

.NET7 seu ASM é o seguinte:

; Tests.GetBucket()
       sub       rsp,28
       mov       rcx,[rcx+8]
       mov       eax,2A
       mov       edx,[rcx+8]
       mov       r8d,edx
       xor       edx,edx
       idiv      r8
       cmp       rdx,r8
       jae       short M00_L00
       mov       eax,[rcx+rdx*4+10]
       add       rsp,28
       ret
M00_L00:
       call      CORINFO_HELP_RNGCHKFAIL
       int       3

Ele ainda executa a verificação de limites, mas o JIT do .NET8 pode reconhecer automaticamente que o índice de (uint)hashcode%buckets.Length não pode exceder o comprimento da matriz, que é buckets.Length. Portanto, o .NET8 pode omitir a verificação de limites, como segue.NET8 ASM

; Tests.GetBucket()
       mov       rcx,[rcx+8]
       mov       eax,2A
       mov       r8d,[rcx+8]
       xor       edx,edx
       div       r8
       mov       eax,[rcx+rdx*4+10]
       ret

Vejamos outro exemplo:

public class Tests
{
    private readonly string _s = "\"Hello, World!\"";

    public bool IsQuoted() => IsQuoted(_s);

    private static bool IsQuoted(string s) =>
    s.Length >= 2 && s[0] == '"' && s[^1] == '"';
}

IsQuoted verifica se a string tem pelo menos dois caracteres, e o início e o fim da string terminam com aspas. s[^1] significa s[s.Length - 1], que é o comprimento da string. .NET7 ASM é o seguinte:

; Tests.IsQuoted(System.String)
       sub       rsp,28
       mov       eax,[rcx+8]
       cmp       eax,2
       jl        short M01_L00
       cmp       word ptr [rcx+0C],22
       jne       short M01_L00
       lea       edx,[rax-1]
       cmp       edx,eax
       jae       short M01_L01
       mov       eax,edx
       cmp       word ptr [rcx+rax*2+0C],22
       sete      al
       movzx     eax,al
       add       rsp,28
       ret
M01_L00:
       xor       eax,eax
       add       rsp,28
       ret
M01_L01:
       call      CORINFO_HELP_RNGCHKFAIL
       int       3

Observe como o .NET7 realmente executa uma verificação de limites, mas verifica apenas uma porque possui apenas um salto de instrução jae. Por que é isso? O JIT já sabe que não há necessidade de realizar uma verificação de limites em s[0], porque s.Length >= 2 já foi verificado e nenhuma verificação é necessária enquanto o índice for menor que 2 (porque o índice não tem sinal e não há números negativos). No entanto, a verificação de limite ainda é realizada em s[s.Length - 1], portanto, embora o .NET7 também seja astuto, ele não é completo o suficiente.
Vamos dar uma olhada no .NET8 que é completamente sexy

; Tests.IsQuoted(System.String)
       mov       eax,[rcx+8]
       cmp       eax,2
       jl        short M01_L00
       cmp       word ptr [rcx+0C],22
       jne       short M01_L00
       dec       eax
       cmp       word ptr [rcx+rax*2+0C],22
       sete      al
       movzx     eax,al
       ret
M01_L00:
       xor       eax,eax
       ret

Sem nenhuma verificação de limites, o JIT não apenas percebe que s[0] é seguro porque s.Length >= 2 foi verificado. Como verifiquei s.Length >= 2, também percebi que s.length> s.Length-1 >=1. Portanto, não há necessidade de verificação de limites, tudo é otimizado.

Você pode ver o quão poderosa é a otimização de desempenho do .NET8. Basicamente, ela esgota o mecanismo JIT e permite que ele seja otimizado ao máximo grau de inteligência.


Clique abaixo para participar do grupo de discussão técnica:

Bem-vindo ao grupo de intercâmbio de tecnologia .NET

Spring Boot 3.2.0 é lançado oficialmente. A falha de serviço mais séria na história do Didi. O culpado é o software subjacente ou “redução de custos e aumento de risos”? Os programadores adulteraram os saldos do ETC e desviaram mais de 2,6 milhões de yuans por ano. Os funcionários do Google criticaram o chefão depois de deixar seus empregos. Eles estavam profundamente envolvidos no projeto Flutter e formularam padrões relacionados ao HTML. O Microsoft Copilot Web AI será lançado oficialmente em 1º de dezembro, com suporte para PHP chinês 8.3 GA Firefox em 2023 Rust Web framework Rocket se tornou mais rápido e lançou v0.5: suporta assíncrono, SSE, WebSockets, etc. O processador de desktop Loongson 3A6000 é lançado oficialmente, a luz da produção doméstica! Broadcom anuncia aquisição bem-sucedida da VMware
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/u/5407571/blog/10314437
Recomendado
Clasificación