Pergunta

Na este artigo incondicional há uma função find_maskwidth() que, basicamente, detecta o número de bits necessários para representar valores dictinct itemCount:

unsigned int find_maskwidth( unsigned int itemCount )
{
    unsigned int maskWidth, count = itemCount;
    __asm {
        mov eax, count
        mov ecx, 0
        mov maskWidth, ecx
        dec eax
        bsr cx, ax
        jz next
        inc cx
        mov maskWidth, ecx
    next:
    }
    return maskWidth; 
} 

a questão é por que eles usam ax e cx registros em vez de eax e ecx?

Foi útil?

Solução

Eu só posso imaginar que eles esperam para sempre apenas lidar com campos que não mais do que 16 bits tem. Desde que está sendo usado para determinar o número de bits que podem ser usados ??para relatar coisas como o número de núcleos ou processadores lógicos em um pacote que provavelmente vai ser um pouco antes que transborde acima 65535.

Eu só espero que alguém não decidir usar a rotina para um propósito mais geral.

E apenas uma FYI - se você quiser fazer algo como isto sem deixar cair a x86 assembly (embora eu acho que para o propósito do artigo, sendo não-portátil é praticamente um dado), a Bit página girando Hacks tem coberto.

Outras dicas

Eu acho que os dados originais é de apenas 16 bits de largura. Desde cx está recebendo um número de bit, não há nenhuma chance de que ele poderia ser maior do que um número muito pequeno de qualquer maneira.

É interessante notar que, em um nível os opcodes para 16 bits e 32 bits ia32 instruções são as mesmas, exceto para o byte de prefixo, por isso, é mais eficiente para emitir todos os-32 ou instruções bit all-16, dependendo do modo em que você está. Acho que é por isso que você fez a pergunta ...

Dado o fato de que este código é realmente muito mal escrito (não há absolutamente nenhuma necessidade para o maskWidth e contar variáveis, por exemplo - para além de tornar o código confuso) Eu acho que você pode ter certeza que ele é apenas mais um " coisa ruim" neste código.

A rotina bascially determina o binário logaritmo da ITEMCOUNT (base dois).

Ele vai entregar valores completamente errados, se ITEMCOUNT> 2 ^ 16. Não saturar ou algo assim, é apenas errado liso. O parâmetro de entrada é "int não assinado", o que torna ainda mais errado. Então, ele vai parar de trabalhar em mais 65536 núcleos.

Meu palpite é, alguém na Intel cavou-se alguma peça muito antiga de código, que remonta a 16 vezes bit, sem realmente entendê-lo, e é usado, porque 65536 será suficiente para sempre, exatamente como 640k de memória será suficiente para sempre ou como dois dígitos números de ano será suficiente até o fim dos tempos.

Eu diria que é porque o autor deste código, provavelmente, realmente não sabia o que estava fazendo :-). As versões destas instruções de 16 bits são mais longos, e não mais rápido. Na verdade, eles provavelmente causa uma barraca de registo parcial sobre a próxima instrução que usa ECX (ou seja, o MOV).

Observe também que o salto pode ser movido com segurança uma instrução mais cedo (após o DEC), como a dezembro já estabelece ZF quando sua saída é zero. Isso pode simplificar o código um pouco.

Então é assim que eu iria escrever este trecho de código:

  mov eax, [count]
  xor ecx, ecx
  dec eax
  jz next
  bsr ecx, eax
  inc ecx
next:
  mov [maskWidth], ecx

Além disso, a motivação para deixar cair a montagem aqui parece estar usando a instrução BSR, que não tem qualquer equivalente na língua C ou biblioteca. É possível evitar a utilização de montagem por meio de um função intrínseca específicos do compilador para esta finalidade. Enquanto estas são inerentemente não portável, nem está em linha de montagem.

Em GCC função equivalente ficaria assim:

unsigned int find_maskwidth(unsigned int itemCount)
{
   if(itemCount <= 1)
      return 0;
   else
      return 32 - __builtin_clz(itemCount - 1);
}

Muito mais legível, não é?

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