Por que os tipos inteiros rápidos são mais rápidos que os outros tipos inteiros?


107

Na ISO / IEC 9899: 2018 (C18), é declarado em 7.20.1.3:

7.20.1.3 Tipos inteiros de largura mínima mais rápidos

1 Cada um dos seguintes tipos designa um tipo inteiro que geralmente é o mais rápido 268) para operar com todos os tipos inteiros que tenham pelo menos a largura especificada.

2 O nome typedef int_fastN_tdesigna o tipo inteiro assinado mais rápido com uma largura de pelo menos N. O nome typedef uint_fastN_tdesigna o tipo inteiro mais rápido sem sinal com uma largura de pelo menos N.

3 Os seguintes tipos são necessários:

int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t, uint_fast32_t,uint_fast64_t

Todos os outros tipos deste formulário são opcionais.


268) Não é garantido que o tipo designado seja o mais rápido para todos os fins; se a implementação não tiver motivos claros para escolher um tipo em detrimento de outro, simplesmente escolherá um tipo inteiro que atenda aos requisitos de sinal e largura.


Mas não se afirma por que esses tipos inteiros "rápidos" são mais rápidos.

  • Por que esses tipos inteiros rápidos são mais rápidos que os outros tipos inteiros?

Marquei a pergunta com C ++, porque os tipos inteiros rápidos também estão disponíveis no C ++ 17 no arquivo de cabeçalho de cstdint. Infelizmente, na ISO / IEC 14882: 2017 (C ++ 17), não existe uma seção dessa explicação; Eu havia implementado essa seção de outra maneira no corpo da pergunta.


Informação: em C, eles são declarados no arquivo de cabeçalho de stdint.h.


24
O ponto principal aqui é que esses tipos inteiros não são separados, tipos magicamente mais rápidos. Eles são apenas aliases para qualquer tipo existente normal que seja o mais rápido nessa máquina para essa operação.
mtraceur 6/01

3
O compilador emite códigos de operação da CPU para carregar, armazenar, mascarar e modificar locais de memória e registros de tamanhos específicos; isso é tudo que a CPU vê. O sistema operacional não tem nada a ver com isso. É tudo o que o compilador faz, exatamente como se você tivesse especificado o typedef especificado. (I suponha que um compilador é permitido para processá-lo internamente de forma diferente - talvez de forma mais eficiente, se isso é possível - do que um typedef usuário, contanto que não há nenhuma diferença visível no comportamento.)
Peter - Reintegrar Monica

11
@ RobertS-ReinstateMonica Para ser mais preciso, esses "aliases" são apenas typedef declarações. Então, normalmente , é feito no nível de biblioteca padrão. Obviamente, o padrão C não impõe nenhuma restrição real ao que eles typedefdevem fazer - por exemplo, uma implementação típica é usar int_fast32_tum sistema typedefde int32 bits, mas um compilador hipotético poderia, por exemplo, implementar um __int_fasttipo intrínseco e prometer fazer algo sofisticado. otimizações para escolher o tipo de máquina mais rápido, caso a caso, para variáveis ​​desse tipo e, em seguida, a biblioteca pode typedeffazer isso.
mtraceur 6/01

11
@ RobertS-ReinstateMonica Sim, é verdade. Você obtém programas de desempenho máximo com sinalizadores de compilação específicos da arquitetura, o que torna o binário menos portátil.
Peter - Restabelecer Monica

11
@ RobertS-ReinstateMonica Será mais eficiente na plataforma em que foi compilado para , não necessariamente no .
HABO 17/01

Respostas:


152

Imagine uma CPU que execute apenas operações aritméticas de 64 bits. Agora imagine como você implementaria uma adição de 8 bits não assinada em tal CPU. Seria necessário envolver mais de uma operação para obter o resultado certo. Nessa CPU, as operações de 64 bits são mais rápidas que as operações em outras larguras de número inteiro. Nessa situação, Xint_fastY_tpresumivelmente , todos podem ser um alias do tipo de 64 bits.

Se uma CPU suportar operações rápidas para tipos inteiros estreitos e, portanto, um tipo mais amplo não for mais rápido que o mais estreito, Xint_fastY_tnão haverá um alias do tipo mais amplo do que o necessário para representar todos os bits Y.

Por curiosidade, verifiquei os tamanhos em uma implementação específica (GNU, Linux) em algumas arquiteturas. Eles não são iguais em todas as implementações na mesma arquitetura:

┌────╥───────────────────────────────────────────────────────────┐
 Y     sizeof(Xint_fastY_t) * CHAR_BIT                         
    ╟────────┬─────┬───────┬─────┬────────┬──────┬────────┬─────┤
     x86-64  x86  ARM64  ARM  MIPS64  MIPS  MSP430  AVR 
╞════╬════════╪═════╪═══════╪═════╪════════╪══════╪════════╪═════╡
 8   8       8    8      32   8       8     16      8   
 16  64      32   64     32   64      32    16      16  
 32  64      32   64     32   64      32    32      32  
 64  64      64   64     64   64      64    64      64  
└────╨────────┴─────┴───────┴─────┴────────┴──────┴────────┴─────┘

Observe que, embora as operações nos tipos maiores possam ser mais rápidas, esses tipos também ocupam mais espaço no cache e, portanto, seu uso não gera necessariamente melhor desempenho. Além disso, nem sempre se pode confiar que a implementação fez a escolha certa em primeiro lugar. Como sempre, a medição é necessária para obter melhores resultados.


Captura de tela da tabela, para usuários do Android:

Captura de tela da tabela acima

(O Android não possui caracteres de desenho de caixa na fonte mono - ref )


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Samuel Liew

@RobertSsupportsMonicaCellio Não. "Não é o mesmo em todas as arquiteturas" também é verdadeiro, mas é imediatamente evidente a partir dos dados mostrados, portanto não considero necessário afirmar o óbvio. Eu só mostrei valores de uma implementação e, de fato, outras terão opções diferentes. Verifique, por exemplo, x86-64 no Windows. Você encontrará tamanhos diferentes em comparação ao que foi mostrado aqui.
eerorika 30/01

@RobertSsupportsMonicaCellio Na minha opinião, esses comentários são relevantes para a resposta e são apropriados aqui. Eu deixaria um moderador movê-los, se eles sentissem a necessidade de fazê-lo.
eerorika 30/01

11

Eles não são, pelo menos não de forma confiável.

Os tipos rápidos são simplesmente typedefs para tipos regulares, no entanto, cabe à implementação como defini-los. Eles devem ter pelo menos o tamanho solicitado, mas podem ser maiores.

É verdade que em algumas arquiteturas alguns tipos inteiros têm melhor desempenho que outros. Por exemplo, implementações antigas do ARM tinham instruções de acesso à memória para palavras de 32 bits e bytes não assinados, mas não tinham instruções para meias palavras ou bytes assinados. As instruções de meia palavra e de byte assinado foram adicionadas mais tarde, mas ainda têm opções de endereçamento menos flexíveis, porque precisavam ser colocadas no espaço de codificação sobressalente. Além disso, todas as instruções reais de processamento de dados no ARM funcionam com palavras; portanto, em alguns casos, pode ser necessário mascarar valores menores após o cálculo para fornecer os resultados corretos.

No entanto, também existe a preocupação concorrente da pressão do cache, mesmo que sejam necessárias mais instruções para carregar / armazenar / processar um valor menor. O valor menor ainda pode ter um desempenho melhor se reduzir o número de falhas de cache.

As definições dos tipos em muitas plataformas comuns não parecem ter sido pensadas. Em particular, as plataformas modernas de 64 bits tendem a ter um bom suporte para números inteiros de 32 bits, mas os tipos "rápidos" geralmente são desnecessariamente 64 bits nessas plataformas.

Além disso, os tipos em C se tornam parte da ABI da plataforma. Portanto, mesmo que um fornecedor de plataforma descubra que fez escolhas idiotas, é difícil alterá-las posteriormente.

Ignore os tipos "rápidos". Se você está realmente preocupado com o desempenho inteiro, compare seu código com todos os tamanhos disponíveis.


7

Os tipos rápidos não são mais rápidos do que todos os outros tipos inteiros - eles são de fato idênticos a algum tipo inteiro "normal" (são apenas um alias para esse tipo) - o tipo que for mais rápido por manter um valor de pelo menos que muitos bits.

Depende da plataforma para qual tipo inteiro cada tipo rápido é um alias para.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.