4

いくつかのステータスフラグを含むArduinoアプリケーション(実際にはライブラリ)があります。元々はそれらをintとして宣言していました(この場合はuint8_tなので8ビットの符号なし文字)。しかし、それらすべてを1つの整数に結合し、ビットマスク操作を使用してステータスを設定およびテストすることもできます。

前者の例:

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

後者の例

#define SHIFT_BIT 0

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

前者の方が読みやすくなりますが、後者の方が効率的です(空間と時間)。この状況では、スペースと時間の効率が常に優先される必要がありますか、それとも必要な場合にのみ発生する必要がある一種の最適化ですか?

(追加した)

完全を期すために、これらのbitWriteなどのマクロの配線定義を次に示します。

#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))
4

9 に答える 9

7

この問題に関するレイモンド・チェンの優れた見解をご覧ください。要約すると、オブジェクトの数と実際にこれらの状態を設定するコールサイトの数に応じて、後者の場合が実際に効率的かどうかを確認するために、詳細な計算を行う必要があります。

読みやすさに関しては、メンバー変数を使用してこれを行っているように見えます。つまり、おそらくそれらを優れた関数にカプセル化したことを意味します。その場合、少なくともクラスを使用している人のコードは見栄えがするので、読みやすさにはあまり関心がありません。ただし、懸念がある場合は、いつでもプライベート関数にカプセル化できます。

于 2009-07-28T22:34:45.230 に答える
5

よくわからないAVR-GCCコンパイラのコンプライアンスに応じて、このようなことを実行して、物事をきれいに保つことができます。

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;
}

フラグint全体にも一度にアクセスしたい場合は、次のようにします。

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

次に、2レベルの間接参照でビットにアクセスできます。variable.bits.flag1<-単一のビットフラグを返すか、単一のレベルでintに相当するフラグ全体を取得します。variable.val<-intを返します。

于 2009-07-28T23:07:56.397 に答える
3

2つの方法に分割することにより、定数HIGHとを使用する必要がなくなると、より明確になります。LOW作るbitSetbitClearメソッドだけ。bitSetビットをに設定し、ビットをHIGHbitClear設定しますLOW。次に、次のようになります。

#define SHIFT_BIT 0

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

そしてもちろん、とがあればHIGH == 1LOW == 0==チェックは必要ありません。

于 2009-07-28T22:36:06.070 に答える
1

私の目には、後者のコードでさえまだかなり読みやすいです。それぞれのフラグに名前を付けることで、コードを簡単に読み取ることができます。

これを行うには、「マジック」ナンバーを使用するのが適切ではありません。

if( _flags | 0x20 ) {  // What does this number mean?
   do_something();
}
于 2009-07-28T22:39:50.743 に答える
1

最適化する必要がない場合は、最適化せずに、最も単純なソリューションを使用してください。

最適化する必要がある場合は、次のことを知っておく必要があります。

  • 最初のバージョンは、ビットを切り替えるのではなく、ビットを設定またはクリアするだけの場合、メモリを読み取る必要がないため、最小限の速度で高速になります。

  • 最初のバージョンは、同時実行性の方が優れています。2番目の例では、読み取り-変更-書き込みがあるため、メモリバイトが同時にアクセスされないようにする必要があります。通常、割り込みを無効にします。これにより、割り込みレイテンシがいくらか増加します。また、割り込みを無効にするのを忘れると、非常に厄介で見つけにくいバグにつながる可能性があります(これまでに遭遇した最も厄介なバグはまさにこの種のものでした)。

  • 最初のバージョンは、各アクセスが単一のロードまたはストア操作であるため、コードサイズが最小限に抑えられています(フラッシュの使用量が少なくなります)。2番目のアプローチでは、追加のビット演算が必要です。

  • 2番目のバージョンは、特にこれらのビットが多い場合に、使用するRAMが少なくなります。

  • 一度に複数のビットをテストする場合(たとえば、設定されたビットの1つ)、2番目のバージョンも高速です。

于 2009-07-29T07:18:43.557 に答える
1

読みやすさ、ビットセット、C ++について話しているのならstd::bitset、そこに何も見つからないのはなぜですか?組み込みプログラマーの種族はビットマスクに非常に慣れており、その純粋な醜さ(種族ではなくマスク:)のために盲目を進化させてきたと理解していますが、マスクとビットフィールドを除いて、標準ライブラリにも非常にエレガントなソリューションがあります。

例:

#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.
于 2009-07-29T07:37:04.907 に答える
1

言うのは簡単すぎますか?

flags ^= bit;
于 2009-08-30T17:21:03.977 に答える
0

ビットフィールドの場合は、論理演算を使用することをお勧めします。これにより、次のことが可能になります。

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

これは現在、後者の効率で前者の外観を持っています。これで、関数ではなくマクロを使用できるようになりました。したがって(これが正しければ、次のようになります)。

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

関数を呼び出すオーバーヘッドがなく、コードが再び読みやすくなります。

私は(まだ)Arduinoで遊んだことはありませんが、この種のもののためのマクロがすでにあるかもしれません、私は知りません。

于 2009-07-28T22:44:48.690 に答える
0

私が最初に懸念しているのは、「#define SHIFT 0」です。マクロではなく定数を使用してみませんか?効率が得られる限り、定数によってタイプを判別できるため、後で変換が不要になることを確認できます。

あなたの技術の効率に関して:-最初に、else節を取り除きます(その値がすでにHIGHであるのになぜビットをHIGHに設定するのですか?)-次に、最初に読み取り可能なもの、インラインセッター/ゲッターが内部でビットマスキングを使用することを好みます仕事をし、効率的で読みやすいでしょう。

ストレージに関しては、C ++の場合、ビットセット(列挙型と組み合わせて)を使用する傾向があります。

于 2009-07-29T12:47:23.437 に答える