ビット設定とコードの可読性
-
20-09-2019 - |
質問
私は 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))
解決
レイモンド陳氏の優れ取るの上のチェックアウトこの問題。要約すると、あなたは、後者の場合は、実際にこれらの状態を設定するどのように多くのcallsites対がありますどのように多くのオブジェクトに応じて、実際にはより効率的であるかどうかを調べるために、いくつかの詳細な計算を行う必要があります。
あなたはメンバ変数でこれをやっているように、限り読みやすくするなど、それはあなたがおそらく素敵な機能でそれらをカプセル化してきたことを意味する、見えます。その場合、私は読みやすさと同じように心配していないだから、少なくともクラスを使用している人々のためのコードが良さそうに見えます。それは心配だ場合しかし、あなたは常にプライベート関数でそれをカプセル化することができます。
他のヒント
私はわからない程度だ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;
単一ビットフラグを返すか、単一レベルでのフラグの全体int型の価値を得るために - variable.bits.flag1
<::これで、間接の2つのレベルのビットにアクセスすることができますvariable.val
< - 戻り値int
HIGH
とLOW
を使用する必要性を削除する場合はこれは、明確にすることができます。ただ、bitSet
とbitClear
メソッドを作ります。 bitSet
はHIGH
するビットをセットし、bitClear
はLOW
にビットをセットします。そして、それはなります:
#define SHIFT_BIT 0
if (bitRead(_flags, SHIFT_BIT) == HIGH)
{
bitClear(_flags, SHIFT_BIT);
}
else
{
bitSet(_flags, SHIFT_BIT);
}
あなただけHIGH == 1
とLOW == 0
を持っている場合は、そしてもちろん、あなたは==チェックは必要ありません。
私の目には、あなたにも後者のコードはまだかなり読みやすいです。あなたの旗のそれぞれに名前を与えることによって、コードは多くの努力なしで読むことができます。
これを行うには貧しい人々の方法は、「マジック」の数字を使用することです
if( _flags | 0x20 ) { // What does this number mean?
do_something();
}
最適化する必要がない場合は、最適化を行わず、最も単純な解決策を使用してください。
最適化する必要がある場合は、何のために最適化するかを知っておく必要があります。
最初のバージョンでは、ビットを切り替えるのではなくビットをセットまたはクリアするだけであれば、メモリを読み取る必要がないため、最小限の速度が向上します。
最初のバージョンの方が優れています。同時並行性。2 つ目では、読み取り、変更、書き込みが行われるため、メモリ バイトが同時にアクセスされないように注意する必要があります。通常は割り込みを無効にするため、割り込みのレイテンシーが多少増加します。また、割り込みを無効にするのを忘れると、非常に厄介で見つけにくいバグが発生する可能性があります (私がこれまでに遭遇した最も厄介なバグは、まさにこの種類のものでした)。
最初のバージョンは、基本的には改善されています。各アクセスは 1 回のロードまたはストア操作であるため、コード サイズ (フラッシュの使用量が少なくなります)。2 番目のアプローチでは、追加のビット操作が必要です。
2 番目のバージョンは、特にこれらのビットが多い場合に使用する RAM の量が少なくなります。
複数のビットを一度にテストしたい場合は、2 番目のバージョンの方が高速です (例:設定されるビットの 1 つです)。
あなたが読みやすさ、ビットセットとC ++を話しているなら、なぜ私はそこでstd::bitset
上で何かを見つけることができませんか?私は組み込みプログラマのレースはビットマスクと非常に快適であり、その膨大なuglyness(マスク、ないレース:)のために失明し進化してきたことを理解しますが、離れてマスクビットフィールドから、標準ライブラリは、あまりにも、非常にエレガントなソリューションを持っています。
例:
#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.
ビットフィールドの場合、それは論理演算を使用することをお勧めしますので、あなたが行うことができます:
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
あなたが関数を呼び出すのオーバーヘッドを持っていない、とのコードが再び読みやすくなります。
私は(まだ)アルドゥイーノでプレイしていませんでしたが、すでにこの種のもののためのマクロがあるかもしれない、私は知らない。
私は私があると心配最初の事を言います: "の#define SHIFT 0" なぜ、むしろマクロよりも定数を利用していませんか?限り効率が得るとして、定数は、このように何の変換は後で必要ないであろうよりも確認して、タイプを決定することができます。
あなたの技術の効率に関しては: - まず、else節を取り除く(その値がすでにHIGHであれば、なぜHIGHにビットを設定します?) - 第二、内部ビットマスクを使用して読めるまず、インラインセッター/ゲッターは、仕事を効率的かつ読みやすいだろう何かを持っていることを好む。
。 ストレージ用として、C ++のために、私は(列挙と組み合わさ)ビット集合を使用する傾向があるだろう。
それは単純すぎるだけと言うことです。
flags ^= bit;