使用uint32_t和GCC原子指令将32位结构联合起来

时间:2014-10-09 14:59:21

标签: c multithreading gcc struct unions

在编写的多线程程序中,我遇到了一些性能问题,锁定争用率很高。

我通过在32位无符号整数中包含一些标志来解决这个问题。

目前我只是将临时变量中的值移位,然后以原子方式将其写入。

但我真的不想记住确切的位移量或者标志所在的位置。

所以我一直想知道我是否可以使用uint32_t和带有相同大小的bitflags的结构进行联合,我不能通过结构访问bitflags并原子地将其写为uint32_t?

下面是关于我如何工作的代码。它确实有效,但我不确定是否允许这样做

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

typedef struct atomic_flags {
    unsigned int        flags1       : 2;
    unsigned int        flags2       : 2;
    unsigned int        flags3       : 2;
    unsigned int        flags4       : 2;
    unsigned int        flags5       : 8;
    unsigned int        reserved     : 16;
}atomic_flags;

union data {
    atomic_flags    i;
    uint32_t        q;
} data;



int main() {
    union data      test1;
    union data      test2;

    test1.i.flags1 = 1;
    test1.i.flags2 = 2; 
    test1.i.flags3 = 3;
    test1.i.flags4 = 2;
    test1.i.flags5 = 241;
    test1.i.reserved = 1337;

    printf("%u\n", test1.q);

    __atomic_store_n(&test2.q, test1.q, __ATOMIC_SEQ_CST);

    printf("test1 flags1: %u\n", test1.i.flags1);
    printf("test1 flags2: %u\n", test1.i.flags2);
    printf("test1 flags3: %u\n", test1.i.flags3);
    printf("test1 flags4: %u\n", test1.i.flags4);
    printf("test1 flags5: %u\n", test1.i.flags5);
    printf("test1 reserved: %u\n", test1.i.reserved);

    printf("test2 flags1: %u\n", test2.i.flags1);
    printf("test2 flags2: %u\n", test2.i.flags2);
    printf("test2 flags3: %u\n", test2.i.flags3);
    printf("test2 flags4: %u\n", test2.i.flags4);
    printf("test2 flags5: %u\n", test2.i.flags5);
    printf("test2 reserved: %u\n", test2.i.reserved);

}

或者甚至可能这样?

__atomic_store_n(&test2.i.flags1, 2, __ATOMIC_SEQ_CST);

1 个答案:

答案 0 :(得分:1)

这是实施定义。

如果你想让所有的掩盖和移动变得更容易并减少出错的可能性,那么更坚固(但更丑陋)的方式就是让预处理器帮助你:

/*
 * widths of the bitfields; these values can be changed independently of anything
 * else, provided that the total number of bits does not exceed 32.
 */
#define FLAG_flag1_BITS  2
#define FLAG_flag2_BITS  2
#define FLAG_flag3_BITS  2
#define FLAG_flag4_BITS  2
#define FLAG_flag5_BITS  8
/* Macro evaluating to the number of bits in the named flag */
#define FLAG_BITS(flagname) (FLAG_ ## flagname ## _BITS)

/*
 * Positions of the flags in the overall bitmask; these adapt to the flag widths
 * above, but a new macro (with the same pattern) will be needed if a bitfield
 * is added.
 */
#define FLAG_flag1_SHIFT 0
#define FLAG_flag2_SHIFT (FLAG_flag1_SHIFT + FLAG_flag1_BITS)
#define FLAG_flag3_SHIFT (FLAG_flag2_SHIFT + FLAG_flag2_BITS)
#define FLAG_flag4_SHIFT (FLAG_flag3_SHIFT + FLAG_flag3_BITS)
#define FLAG_flag5_SHIFT (FLAG_flag4_SHIFT + FLAG_flag4_BITS)
/* Macro evaluating to the position of the named flag in the overall bitfield */
#define FLAG_SHIFT(flagname) (FLAG_ ## flagname ## _SHIFT)

/* evaluates to a bitmask for selecting the named flag's bits from a bitfield */
#define FLAG_MASK(flagname) \
    ((~(((uint32_t) 0xffffffff) << FLAG_BITS(flagname))) << FLAG_SHIFT(flagname))
/* evaluates to a bitfield having the specified flag set to the specified value */
#define FLAG(flagname, v) ((v << FLAG_SHIFT(flagname)) & FLAG_MASK(flagname))

/* macro to set the specified flag in the specified bitfield to the specified value */
#define SET_FLAG(flagname, i, v) \
    do { i = (i & ~FLAG_MASK(flagname)) | FLAG(flagname, v); } while (0)
/* macro to retrieve the value of the specified flag from the specified bitfield */
#define GET_FLAG(flagname, i) (((i) & FLAG_MASK(flagname)) >> FLAG_SHIFT(flagname))

/* usage example */
int function(uint32_t bitfield) {
    uint32_t v;

    SET_FLAG(flag2, bitfield, 1);
    v = GET_FLAG(flag5, bitfield);
}

虽然这涉及到一堆巨大的宏,但它主要由第一组驱动,它给出了位域宽度。基本上所有这些都将编译为相同的移位和屏蔽操作,无论如何,因为计算将主要由预处理器和/或编译器执行。实际使用非常简单。