O que é o modo mais rápido (s) ao circuito através de um grande pedaço de dados numa base de por bit

StackOverflow https://stackoverflow.com/questions/418266

Pergunta

Estou executando através de um bloco de memória de binário byte-wise de dados.

Atualmente eu estou fazendo algo parecido com isto:

for (i = 0; i < data->Count; i++)
{   
    byte = &data->Data[i];
    ((*byte & Masks[0]) == Masks[0]) ? Stats.FreqOf1++; // syntax incorrect but you get the point.
    ((*byte & Masks[1]) == Masks[1]) ? Stats.FreqOf1++;
    ((*byte & Masks[2]) == Masks[2]) ? Stats.FreqOf1++;
    ((*byte & Masks[3]) == Masks[3]) ? Stats.FreqOf1++;
    ((*byte & Masks[4]) == Masks[4]) ? Stats.FreqOf1++;
    ((*byte & Masks[5]) == Masks[5]) ? Stats.FreqOf1++;
    ((*byte & Masks[6]) == Masks[6]) ? Stats.FreqOf1++;
    ((*byte & Masks[7]) == Masks[7]) ? Stats.FreqOf1++;
}

Onde Máscaras é:

for (i = 0; i < 8; i++)
{
    Masks[i] = 1 << i;
}

(I alguma forma não conseguem fazê-lo tão rápido em um loop ou em uma função inline, então eu escrevi-o para fora.)

Alguém tem alguma sugestão sobre como melhorar este primeiro loop? Estou bastante inexperiente com a obtenção para baixo em pedaços.

Isto pode parecer uma coisa estúpida de se fazer. Mas eu estou no processo de implementação de um algoritmo de compressão. Eu só quero ter a parte pouco acesso a baixo direito.

Obrigado!

PS: Isto está em no compilador Visual Studio 2008. Portanto, seria bom se as sugestões aplicada a esse compilador.

PPS: Eu só percebi que eu não preciso incrementar duas contagens. Um seria o suficiente. Em seguida, calcular a diferença para o total de bits de no final. Mas isso seria específico para apenas contando. O que eu realmente quero feito rapidamente é a extração bit.

EDIT: A idéia tabela de pesquisa que foi antecipada é bom. Eu percebo que embora eu representava o mal pergunta do título. Porque no final o que eu quero fazer é não contar os bits, mas o acesso cada bit tão rápido quanto possível.

OUTRO EDIT: É possível avançar um ponteiro por apenas um bit nos dados?

OUTRO EDIT: Obrigado por todas as suas respostas até agora.

O que eu quero implementar nas próximas etapas é um nonsophisticated binário aritmética codificador que não analisar o contexto. Então eu só estou interessado em bits individuais para agora. Eventualmente, ele se tornará um BAC Context-adaptive mas vou deixar isso para mais tarde.

Processamento de 4 bytes em vez de um byte poderiam ser uma opção. Mas um laço sobre 32 bits é caro também, não é?

Foi útil?

Solução

Eu realmente não entendo o que você está tentando fazer. Mas se você só quer ter acesso aos bits de um bitmap, você pode usá-los (não testado !!!) funções:

#include <stddef.h>

_Bool isbitset(unsigned char * bitmap, size_t idx)
{
    return bitmap[idx / 8] & (1 << (idx % 8)) ? 1 : 0;
}

void setbit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] |= (1 << (idx % 8));
}

void unsetbit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] &= ~(1 << (idx % 8));
}

void togglebit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] ^= (1 << (idx % 8));
}

Editar: Ok, eu pensar Eu entendo o que você quer fazer: iteração rápida sobre uma seqüência de bits. Portanto, nós não queremos usar as funções de acesso aleatório de cima, mas ler uma palavra inteira de dados de uma vez.

Você pode usar qualquer tipo inteiro não assinado que você gosta, mas você deve escolher um que é provável que correspondem ao tamanho da palavra de sua arquitetura. Eu vou com uint_fast32_t de stdint.h:

uint_fast32_t * data = __data_source__;
for(; __condition__; ++data)
{
    uint_fast32_t mask = 1;
    uint_fast32_t current = *data;
    for(; mask; mask <<= 1)
    {
        if(current & mask)
        {
            // bit is set
        }
        else
        {
            // bit is not set
        }
    }
}

A partir do circuito interno, você pode definir o bit com

*data |= mask;

unset o bit com

*data &= ~mask;

e alternar o bit com

*data ^= mask;

Aviso: O código pode se comportar de forma inesperada em arquiteturas grandes-endian

Outras dicas

A maneira mais rápida é provavelmente para construir uma tabela de referência de valores de bytes versus o número de bits definidos em que byte. Pelo menos essa era a resposta quando eu o entrevistei no Google.

Veja o seguinte link para uma dúzia de coisas relacionadas bit: Bit girando Hacks

Use uma tabela que mapeia cada valor de byte (256) para o número de 1s na mesma. (O # de 0 do é apenas (8 - # de 1 de)). Em seguida, repetir os bytes e efectuar uma única pesquisa para cada byte, em vez de várias pesquisas e comparações. Por exemplo:

int onesCount = 0;
for (i = 0; i < data->Count; i++)
{   
    byte = &data->Data[i];
    onesCount += NumOnes[byte];
}
Stats.FreqOf1 += onesCount;
Stats.FreqOf0 += (data->Count * 8) - onesCount;

Você pode usar uma tabela de pesquisa pré-computadas, ou seja:

static int bitcount_lookup[256] = { ..... } ; /* or make it a global and compute the values in code */

...

for( ... ) 
   byte = ... 
   Stats.FreqOf1 += bitcount_lookup[byte];

Aqui é um método como contar os bits 1 de um inteiro de 32 bits (com base no método Integer.bitCount(i) de Java):

unsigned bitCount(unsigned i) {
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i + (i >> 4)) & 0x0f0f0f0f;
    i = i + (i >> 8);
    i = i + (i >> 16);
    return i & 0x3f;
}

Então você pode lançar seus dados para int e avançar em 4 etapas bytes.

Aqui é um simples que eu chicoteado até em apenas um valor de 32 bit único, mas você pode ver que não seria difícil de se adaptar a qualquer número de bits ....

int ones = 0;
int x = 0xdeadbeef;
for(int y = 0;y < 32;y++)
{
    if((x & 0x1) == 0x1) ones++;
    x = (x >> 1);
}

printf("%x contains %d ones and %d zeros.\n", x, ones, 32-ones);

Observe no entanto, que modifica o valor no processo. Se você estiver fazendo isso em dados que você precisa para manter, então você precisa fazer uma cópia do primeiro.

Fazer isso em __asm ??provavelmente seria melhor, talvez mais rápido do caminho, mas é difícil dizer com a forma como o compilador pode otimizar ...

Com cada solução que você considerar, cada um terá inconvenientes. A tabela de pesquisa ou um shifter bits (como o meu), ambos têm desvantagens.

Larry

ttobiass - Tenha em mente as suas funções inline são importantes em aplicações como você está falando, mas há coisas que você precisa manter em mente. Você CAN obter o desempenho fora do código embutido, basta lembrar algumas coisas.

  • em linha no modo de depuração não existe. (A menos que você forçá-lo)
  • o compilador funções inline como lhe aprouver. Muitas vezes, se você diga a ele para inline uma função, não pode fazê-lo em tudo. Mesmo se você usar __forceinline. Verifique MSDN para mais informações sobre inlining.
  • Somente certas funções pode até ser embutido. Por exemplo, você não pode em linha uma função recursiva.

Você poderá obter o seu melhor desempenho de suas configurações de projeto para a linguagem C / C ++, e como você construir o seu código. Neste ponto, é importante compreender Heap vs. operações Pilha, chamando convenções, alinhamento de memória, etc.

Eu sei que isso não responder à sua pergunta exatamente, mas você menciona desempenho, e como obter o melhor desempenho, e essas coisas são fundamentais.

Para participar da ligação vagão: pedaços contando

Se isto não é um caso de otimização prematura e você realmente precisa para espremer cada último femtosegundo, então você é provavelmente melhor fora com uma matriz estática de 256 elemento que você preencher uma vez com o bit de contagem de cada valor de byte , então

Stats.FreqOf1 + = bitCountTable [byte]

e quando o circuito é feito:

Stats.FreqOf0 = ((data-> Conde * 8) - Stats.FreqOf1)

Há um capítulo inteiro sobre as diferentes técnicas para isso no livro bonito Código . Você pode ler (a maioria) que no Google livros começando aqui .

A maneira mais rápida para extrair os bits é usar:

bitmask= data->Data[i];

while (bitmask)
{
    bit_set_as_power_of_two= bitmask & -bitmask;
    bitmask&= bitmask - 1;
}

Se você quiser apenas para contar bits definidos, a LUT em cache por seria rápido, mas você também pode fazê-lo em tempo constante com o método bit contagem intercalados em o link nesta resposta .

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