Pergunta

Em uma CPU de 32 bits, um inteiro tem 4 bytes e um inteiro curto tem 2 bytes.Se eu estiver escrevendo um aplicativo C/C++ que usa muitos valores numéricos que sempre caberão no intervalo fornecido de um número inteiro curto, é mais eficiente usar números inteiros de 4 bytes ou números inteiros de 2 bytes?

Ouvi dizer que números inteiros de 4 bytes são mais eficientes, pois se ajustam à largura de banda do barramento da memória até a CPU.No entanto, se eu estiver somando dois números inteiros curtos, a CPU empacotaria ambos os valores em uma única passagem em paralelo (abrangendo assim a largura de banda de 4 bytes do barramento)?

Foi útil?

Solução

Sim, você definitivamente deve usar um número inteiro de 32 bits em uma CPU de 32 bits, caso contrário, ele pode acabar mascarando os bits não utilizados (ou seja, sempre fará as contas em 32 bits e depois converterá a resposta para 16 bits)

Ele não fará duas operações de 16 bits ao mesmo tempo para você, mas se você mesmo escrever o código e tiver certeza de que ele não transbordará, você poderá fazer isso sozinho.

Editar:Devo acrescentar que também depende um pouco da sua definição de "eficiente".Embora seja capaz de realizar operações de 32 bits mais rapidamente, é claro que você usará o dobro da memória.

Se eles estiverem sendo usados ​​para cálculos intermediários em um loop interno em algum lugar, use 32 bits.Se, no entanto, você estiver lendo isso do disco, ou mesmo se tiver que pagar apenas por uma falta de cache, ainda pode funcionar melhor usar números inteiros de 16 bits.Tal como acontece com todas as otimizações, só há uma maneira de saber: perfile-o.

Outras dicas

Se você tiver uma grande variedade de números, escolha o menor tamanho que funcione.Será mais eficiente trabalhar com uma matriz de shorts de 16 bits do que com ints de 32 bits, pois você obtém o dobro da densidade do cache.O custo de qualquer extensão de sinal que a CPU tenha que fazer para trabalhar com valores de 16 bits em registradores de 32 bits é trivialmente insignificante comparado ao custo de uma falta de cache.

Se você estiver simplesmente usando variáveis ​​de membro em classes misturadas com outros tipos de dados, então será menos claro, pois os requisitos de preenchimento provavelmente removerão qualquer benefício de economia de espaço dos valores de 16 bits.

Se você estiver usando "muitos" valores inteiros, o gargalo no seu processamento poderá ser largura de banda na memória. Os números inteiros de 16 bits empacotam mais firmemente o cache de dados e, portanto, seriam uma vitória de desempenho.

Se você está com um número de número de dados muito grande, você deve ler O que todo programador deve saber sobre memória Por Ulrich Drepper. Concentre -se no capítulo 6, sobre maximizar a eficiência do cache de dados.

Uma CPU de 32 bits é uma CPU que geralmente opera com valores de 32 bits internamente, mas isso não significa que seja mais lento ao executar a mesma operação com um valor de 8/16 bits. x86, por exemplo, ainda compatível com o 8086, pode operar em frações de um registro. Isso significa que, mesmo que um registro seja de 32 bits, ele pode operar apenas nos primeiros 16 ou nos primeiros 8 bits desse registro e não haverá desaceleração. Esse conceito foi adotado por x86_64, onde os registros são de 64 bits, mas eles ainda podem operar apenas nos primeiros 32, 16 ou 8 bits.

O X86 CPUS sempre carrega uma linha de cache inteira da memória, se ainda não estiver em cache, e uma linha de cache é maior que 4 byte de qualquer maneira (para CPUs de 32 bits em vez de 8 ou 16 bytes) e, assim, carregar 2 byte da memória é igualmente rápido como Carregando 4 byte da memória. Se o processamento de muitos valores da memória, os valores de 16 bits podem realmente ser muito mais rápidos que os valores de 32 bits, pois existem menos transferências de memória. Se uma linha de cache for de 8 byte, existem quatro valores de 16 bits por linha de cache, mas apenas dois valores de 32 bits; portanto, ao usar INTs de 16 bits, você tem um acesso à memória a cada quatro valores, usando 32 bits INTs, você tem um a cada dois valores , resultando em duas vezes mais transferências para o processamento de uma grande matriz INT.

Outras CPUs, como o PPC, por exemplo, não podem processar apenas uma fração de um registro, sempre processam o registro completo. No entanto, essas CPUs geralmente possuem operações de carga especiais que permitem, por exemplo, carregam um valor de 16 bits da memória, expandem -as para 32 bits e escrevam para um registro. Mais tarde, eles têm uma operação especial da loja que pega o valor do registro e armazena apenas os últimos 16 bits de volta à memória; Ambas as operações precisam de apenas um ciclo da CPU, assim como uma carga/loja de 32 bits precisaria, para que não haja diferença de velocidade. E como o PPC só pode executar operações aritméticas nos registros (diferentemente do X86, que também podem operar diretamente na memória), esse procedimento de carga/loja ocorre de qualquer maneira, se você usa INTs de 32 bits ou 16 bits.

A única desvantagem, se você acionar várias operações em uma CPU de 32 bits que só pode operar em registros completos, é que o resultado de 32 bits da última operação pode ser "cortado" para 16 bits antes da próxima operação ser realizada, caso contrário, o resultado pode não estar correto. Esse corte é apenas um único ciclo de CPU (uma operação e uma operação), e os compiladores são muito bons em descobrir quando esse corte é realmente necessário e, ao deixar de fora, não terá influência no resultado final , portanto, esse corte não é realizado após todas as instruções, ele é executado apenas se realmente inevitável. Algumas CPUs oferecem várias instruções "aprimoradas" que tornam esse corte desnecessário e eu já vi bastante código na minha vida, onde eu esperava esse corte, mas olhando para o código de montagem gerado, o compilador encontrou uma maneira de Evite -o completamente.

Então, se você espera uma regra geral aqui, terei que decepcioná -lo. Nem se pode dizer com certeza que as operações de 16 bits são igualmente rápidas a 32 operações de bits, nem ninguém pode dizer com certeza que as operações de 32 bits sempre serão mais rápidas. Depende também do que exatamente o seu código está fazendo com esses números e como está fazendo isso. Vi benchmarks, onde as operações de 32 bits eram mais rápidas em certos CPUs de 32 bits do que o mesmo código com operações de 16 bits, mas também já vi o oposto sendo verdadeiro. Mesmo mudar de um compilador para outro ou a atualização da versão do compilador já pode mudar tudo novamente. Só posso dizer o seguinte: Quem afirma que trabalhar com shorts é significativamente mais lento do que trabalhar com INTs, fornecerá um código -fonte de amostra para essa reivindicação e nome da CPU e compilador que ele usou para testar, já que nunca experimentei algo assim dentro sobre os últimos 10 anos. Pode haver algumas situações, onde trabalhar com INTs talvez seja 1-5% mais rápido, mas qualquer coisa abaixo de 10% não é "significativa" e a questão é: vale a pena desperdiçar o dobro da memória em alguns casos apenas porque pode comprar você 2% de desempenho? Eu não acho.

Depende. Se você estiver ligado à CPU, operações de 32 bits em uma CPU de 32 bits serão mais rápidas que 16 bits. Se você estiver ligado à memória (especificamente se tiver muitas erros de cache L2), use os menores dados em que você pode espremer.

Você pode descobrir qual você está usando um perfil que medirá as erros da CPU e L2 Vtune da Intel. Você executará seu aplicativo duas vezes com a mesma carga e ele mesclará as 2 execuções em uma visão dos pontos de acesso do seu aplicativo e você poderá ver para cada linha de código quantos ciclos foram gastos nessa linha. Se estiver em uma linha de código cara, você verá 0 cache errar, você está vinculado à CPU. Se você vir toneladas de erros, você está vinculado à memória.

Não ouça o conselho, tente.

Provavelmente, isso dependerá muito do hardware/compilador que você está usando. Um teste rápido deve fazer um pouco de trabalho dessa questão. Provavelmente menos tempo para escrever o teste do que escrever a pergunta aqui.

Se você estiver operando em um grande conjunto de dados, a maior preocupação é a pegada de memória. Um bom modelo neste caso é assumir que a CPU é infinitamente rápida e gastar seu tempo se preocupando com a quantidade de dados que deve ser movida de acordo com/para a memória. De fato, as CPUs agora são tão rápidas que às vezes é mais eficiente codificar (por exemplo, compactar) os dados. Dessa forma, a CPU faz (potencialmente muito) mais trabalho (decodificação/codificação), mas a largura de banda da memória é substancialmente reduzida.

Portanto, se o seu conjunto de dados for grande, você provavelmente será melhor usando números inteiros de 16 bits. Se a sua lista for classificada, você poderá projetar um esquema de codificação que envolva uma codificação diferencial ou com comprimento de corrida, o que reduzirá ainda mais a largura de banda da memória.

Quando você diz 32 bits, presumo que você quer dizer x86.A aritmética de 16 bits é bastante lenta:o prefixo do tamanho do operando faz a decodificação realmente lento.Portanto, não torne suas variáveis ​​temporárias curtas como int ou int16_t.

No entanto, o x86 pode carregar com eficiência números inteiros de 16 e 8 bits em registradores de 32 ou 64 bits.(movzx/movsx:zero e extensão de sinal).Portanto, sinta-se à vontade para usar short int para arrays e campos struct, mas certifique-se de usar int ou long para suas variáveis ​​temporárias.

No entanto, se eu estiver somando dois números inteiros curtos, a CPU empacotaria ambos os valores em uma única passagem em paralelo (abrangendo assim a largura de banda de 4 bytes do barramento)?

Isso é um absurdo.as instruções de carregamento/armazenamento interagem com o cache L1 e o fator limitante é o número de operações;largura é irrelevante.por exemplo.no núcleo2:1 carga e 1 armazenamento por ciclo, independentemente da largura.O cache L1 possui um caminho de 128 ou 256 bits para o cache L2.

Se as cargas são o seu gargalo, uma carga larga que você divide com turnos ou máscaras após o carregamento pode ajudar.Ou use o SIMD para processar dados em paralelo sem descompactá-los após carregá-los em paralelo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top