Pergunta

Eu tenho um Arduino aplicativo (bem, na verdade, uma biblioteca) que tem um número de sinalizadores de status - e, originalmente, eu simplesmente declarou-los como ints (bem uint8_t, de modo 8 bits não assinados caracteres, neste caso).Mas eu poderia ter combinado-as em um número inteiro e usado máscara de operações para definir e testar o estado.

Um exemplo do primeiro:

if (_shift == HIGH)
{
    _shift = LOW;
}
else
{
    _shift = HIGH;
}

um exemplo do segundo

#define SHIFT_BIT 0

if (bitRead(_flags, SHIFT_BIT) == HIGH)
{
   bitWrite(_flags, SHIFT_BIT, LOW);
}
else
{
   bitWrite(_flags, SHIFT_BIT, HIGH);
}

O ex-lê melhor, mas o último é mais eficiente (espaço e tempo).Deve o espaço e o tempo da eficiência de sempre vencer nesta situação, ou é um tipo de otimização que só deve acontecer quando necessário?

(Adicionado)

Para a completude aqui está a Fiação definição desses bitWrite etc.macros:

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
Foi útil?

Solução

Confira Raymond Chen excelente tomar sobre este problema.Em resumo, você precisa fazer algum cálculo detalhado para descobrir se o caso é realmente mais eficiente, dependendo de quantos objetos há vs.quantas callsites na verdade, esses estados.

Quanto a legibilidade, parece que você está fazendo isso com variáveis de membro, o que significa que você, provavelmente, encapsulado-los em bom funções.Nesse caso, eu não estou tão preocupado com a legibilidade, pois pelo menos o código para o povo usando a classe parece bom.No entanto, você sempre pode compactá-lo em funções privadas se é uma preocupação.

Outras dicas

Dependendo da conformidade do compilador AVR-GCC, sobre o qual não tenho certeza, você pode fazer algo assim e manter as coisas boas e limpas.

struct flags {
    unsigned int flag1 : 1;  //1 sets the length of the field in bits
    unsigned int flag2 : 4;
}; 

flags data;

data.flag1 = 0;
data.flag2 = 12;

if (data.flag1 == 1)
{
    data.flag1 = 0;
}
else
{
    data.flag1 = 1;
}

Se você também deseja acesso a toda a bandeira int de uma só vez, então:

union 
{
    struct {
        unsigned int flag1 : 1;  //1 sets the length of the field in bits
        unsigned int flag2 : 4;
    } bits;
    unsigned int val;
} flags;

Você pode acessar um pouco com 2 níveis de indireção: variable.bits.flag1<-Retorna a bandeira de bits únicos ou com um único nível para obter todo o valor de bandeiras: variable.val <-retorna int

Pode ser mais claro se você remover a necessidade de usar constantes HIGH e LOW, dividindo -se em dois métodos. Apenas faça bitSet e bitClear métodos. bitSet define a parte para HIGH, e bitClear define a parte para LOW. Então se torna:

#define SHIFT_BIT 0

if (bitRead(_flags, SHIFT_BIT) == HIGH)
{
    bitClear(_flags, SHIFT_BIT);
}
else
{
    bitSet(_flags, SHIFT_BIT);
}

E, claro, se você apenas tem HIGH == 1 e LOW == 0, então você não precisa do == Verifique.

A meu olho, até o seu último código ainda é bastante legível. Ao dar um nome a cada uma de suas bandeiras, o código pode ser lido sem muito esforço.

Uma maneira ruim de fazer isso seria usar números "mágicos":

if( _flags | 0x20 ) {  // What does this number mean?
   do_something();
}

Se você não precisa para otimizar, não fazê-lo e usar a solução mais simples.

Se você precisa fazer para otimizar, você deve saber o que:

  • A primeira versão seria minimamente mais rápido se você apenas definir ou limpar o bit, em vez de alternar-lo, porque então você não precisa ler a memória.

  • A primeira versão é a melhor de w.r.t.simultaneidade.No segundo você tem de ler-modificar-escrever, então você precisa se certificar de que os bytes de memória não é acessado simultaneamente.Normalmente você desativar as interrupções, o que aumenta a latência da interrupção um pouco.Também, esquecendo-se de desativar as interrupções podem resultar muito desagradável e difícil de encontrar bugs (o pior erro que eu encontrei até agora foi de exatamente esse tipo).

  • A primeira versão é minimamente melhor w.r.t.tamanho do código (menos uso do flash), porque cada acesso é de uma única carga ou operação de loja.A segunda abordagem necessidades adicionais de operações bit.

  • A segunda versão utiliza menos memória RAM, principalmente se você tem um monte desses bits.

  • A segunda versão também é mais rápido, se você quiser testar vários bits de uma só vez (por exemplo,é um dos conjunto de bits).

Se você está falando de legibilidade, bits e C ++, por que não encontro nada sobre std::bitset lá? Entendo que a corrida de programador incorporada é bastante confortável com as máscaras de bits e desenvolveu uma cegueira por sua pura feia (as máscaras, não as corridas :), mas, além de máscaras e campos de bits, a biblioteca padrão também possui uma solução bastante elegante.

Um exemplo:

#include <bitset>

enum tFlags { c_firstflag, c_secondflag, c_NumberOfFlags };

...

std::bitset<c_NumberOfFlags> bits;

bits.set( c_firstflag );
if( bits.test( c_secondflag ) ) {
  bits.clear();
}

// even has a pretty print function!
std::cout << bits << std::endl;// does a "100101" representation.

Para campos de bits, é melhor usar operações lógicas, para que você possa fazer:

if (flags & FLAG_SHIFT) {
   flags &= ~FLAG_SHIFT;
} else {
   flags |= FLAG_SHIFT;
}

Isso agora tem a aparência do primeiro com a eficiência deste último. Agora você poderia ter macros em vez de funções, então (se eu acertar, seria algo como):

#define bitIsSet(flags,bit) flags | bit
#define bitSet(flags,bit) flags |= bit
#define bitClear(flags,bit) flags &= ~bit

Você não tem a sobrecarga de chamar funções, e o código se torna mais legível novamente.

Ainda não brinquei com Arduino (ainda), mas já pode haver macros para esse tipo de coisa, não sei.

Eu diria que a primeira coisa que estou preocupada é: "#Define Shift 0" Por que não fazer uso de uma macro constante e não? No que diz respeito à eficiência, a constante permite determinar o tipo, certificando -se de que não será necessário conversão posteriormente.

Quanto à eficiência da sua técnica: - Primeiro, livre -se da cláusula else (por que definir o bit como alto se seu valor já estiver alto?) - Segundo, prefira ter algo legível primeiro, criadores / getters inlinados usando bits máscara internamente Faria o trabalho, seria eficiente e legível.

Quanto ao armazenamento, para C ++, eu tenderia a usar um bitset (combinado com uma enumeração).

É muito simples apenas dizer:

flags ^= bit;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top