簡體   English   中英

布爾值對位字段的優勢

[英]Advantages of boolean values to bit-fields

我工作的代碼庫很舊。 雖然我們使用c ++ 11編譯幾乎所有內容。 許多代碼是多年前用c編寫的。 在舊的地區開發新課程時,我總是發現自己不得不選擇匹配的舊方法,還是采用更現代的方法。

在大多數情況下,我寧願盡可能使用更現代的技術。 但是,我經常看到的一種常見的老方法是位域,我很難爭論使用它。 我們傳遞了很多消息,在這里很多次,它們充滿了單個位的值。 請看下面的例子:

class NewStructure
{
public:

    const bool getValue1() const
    {
        return value1;
    }

    void setValue1(const bool input)
    {
        value1 = input;
    }

private:
    bool value1;
    bool value2;
    bool value3;
    bool value4;
    bool value5;
    bool value6;
    bool value7;
    bool value8;
};

struct OldStructure
{
    const bool getValue1() const
    {
        return value1;
    }

    void setValue1(const bool input)
    {
        value1 = input;
    }

    unsigned char value1 : 1;
    unsigned char value2 : 1;
    unsigned char value3 : 1;
    unsigned char value4 : 1;
    unsigned char value5 : 1;
    unsigned char value6 : 1;
    unsigned char value7 : 1;
    unsigned char value8 : 1;
};

在這種情況下,新結構的大小為8個字節,舊結構的大小為1個字節。
我添加了“ getter”和“ setter”來說明從用戶角度來看,它們可以相同。 我意識到,也許您可​​以為下一個開發人員提供可讀性,但是除此之外,還有理由避免位字段嗎? 我知道壓縮字段會影響性能,但是由於這些都是字符,因此填充規則仍然存在。

使用位域時需要考慮幾件事。 這些是(重要性順序取決於情況)

  • 性能

設置或讀取時,位域操作會導致性能下降(與直接類型相比)。 一個簡單的代碼生成示例顯示了發出的額外指令: https : //gcc.godbolt.org/z/DpcErN但是,位域提供了更緊湊的數據,這變得對緩存更友好,並且可以完全彌補附加操作的任何弊端。 理解實際性能影響的唯一方法是在實際用例中對實際應用進行基准測試。

  • ABI互操作性

位字段的字節序是實現定義的,因此兩個編譯器生成的相同結構的布局可以不同。

  • 易用性

沒有引用綁定到位域,也不能使用它的地址。 這可能會影響代碼並使其不清楚。

對於您來說,作為程序員,沒有太大的區別。 但是,與訪問單個位相比,訪問整個字節的機器代碼要簡單/短得多,因此使用位域會堆積所生成的代碼。

用偽匯編語言,您的設置方法可能會變成類似以下內容:

    ldb input1,b         ; get the new value into accumulator b
    movb b,value1        ; put it into the variable
    rts                  ; return from subroutine

但這對於位域而言並不容易:

    ldb input1,b        ; get the new value into accumulator b
    movb bitfields,a    ; get current bitfield values into accumulator a
    cmpb b,#0           ; See what to do.
    brz clearvalue1:    ; If it's zero, go to clearing the bit
    orb #$80,a          ; set the bit representing value1.
    bra resume:         ; skip the clearing code.
clearvalue1:
    andb #$7f,a         ; clear the bit representing value1
resume:
    movb a,bitfields    ; put the value back
    rts                 ; return

它必須為您的8個成員的設置者執行此操作,對於getter則必須執行類似操作。 它加起來。 此外,即使是當今最笨拙的編譯器,也可能會內聯完整字節的setter代碼,而不是實際進行子例程調用。 對於位域設置器,這可能取決於您是否要針對速度與空間進行編譯優化。

而且您只問過布爾值。 如果它們是整數位字段,則編譯器必須處理加載,屏蔽先前的值,將值移動以使其對齊到其字段中,屏蔽未使用的位and / or將值放在適當的位置,然后將其寫回到內存中。

那么,為什么要使用一個與另一個?

  • 位域速度較慢,但​​打包數據效率更高。
  • 非位域速度更快,並且需要更少的機器代碼來訪問。

作為開發人員,這是您的判斷力。 如果您要一次在內存中保留許多Structure實例,那么節省內存可能是值得的。 如果您不會一次在內存中擁有該結構的多個實例,那么編譯后的代碼膨脹會抵消內存的節省,並且您正在犧牲速度。

template<typename enum_type,size_t n_bits>
class bit_flags{
    std::bitset<n_bits> bits;
    auto operator[](enum_type bit){return bits[bit];};
    auto& set(enum_type bit)){return set(bit);};
    auto& reset(enum_type bit)){return set(bit);};
     //go on with flip et al...
static_assert(std::is_enum<enum_type>{});
 };

enum class  v_flags{v1,v2,/*...*/vN};

bit_flags<v_flags,v_flags::vN+1> my_flags;

my_flags.set(v_flags::v1);
my_flags.[v_flags::v2]=true;

std::bitsetbool位字段一樣有效。 您可以將其包裝在一個類中,以強制使用enum定義的名稱的每一位。 現在,您有了一個小型但可擴展的實用程序,可用於多個不同的bool標志集。 C ++ 17使它更加方便:

template<auto last_flag, typename enum_type=decltype(last_flag)>
class bit_flags{
    std::bitset<last_flag+1> bits;
    //...
};

bit_flags<v_flags::vN+1> my_flags;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM