объединить 32-битную структуру с атомарными инструкциями uint32_t и GCC

В многопоточной программе, которая была написана, у меня есть некоторые проблемы с производительностью из-за очень высокой конкуренции за блокировку.

Я решил эту проблему, добавив несколько флагов в 32-битное целое число без знака.

в настоящее время я просто немного сдвигаю значения во временной переменной, а затем атомарно записываю ее.

Но мне не очень нравится запоминать точное количество битовых сдвигов или где именно находится какой-либо флаг.

Итак, мне было интересно, могу ли я просто объединить uint32_t и структуру с моими битовыми флагами с одинаковым размером, не могу ли я получить доступ к битовым флагам с помощью структуры и атомарно записать их как 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);

person rowan.G    schedule 09.10.2014    source источник
comment
У меня работает: ideone.com/AVmpEn   -  person Michael M.    schedule 09.10.2014
comment
Да. но если работает, это не значит, что он должен работать ... во всяком случае, не в c   -  person rowan.G    schedule 09.10.2014
comment
Разрешены ли атомарные типы для битовых полей (6.7.2.1). определяется реализацией. Какой компилятор вы используете?   -  person 2501    schedule 09.10.2014
comment
gcc, __atomic_store_n() - это встроенный компилятор. Не могли бы вы сказать мне, где вы берете эту информацию?   -  person rowan.G    schedule 09.10.2014
comment
В документации компилятора;)   -  person mafso    schedule 09.10.2014


Ответы (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);
}

Хотя это включает в себя изумительный стек макросов, он в основном управляется первым набором, который дает ширину битового поля. Практически все это будет компилироваться до тех же операций сдвига и маски, которые вы бы использовали в любом случае, поскольку вычисления будут выполняться в основном препроцессором и / или компилятором. Фактическое использование очень простое.

person John Bollinger    schedule 09.10.2014
comment
Это является реализацией. Стандарт: J.3.9. - person 2501; 09.10.2014
comment
@ 2501, спасибо, я отредактировал свой ответ, чтобы быть твердым по этому поводу. - person John Bollinger; 09.10.2014
comment
Хотя это хороший ответ, в чем преимущество этого вместо использования функций? - person rowan.G; 12.10.2014
comment
@ rowan.G преимущество перед использованием функций состоит в том, что вы избегаете накладных расходов на вызов функции. Даже встроенная функция, вероятно, будет дороже во время выполнения, а не встроенная функция будет намного дороже. Вы также получаете двоичный файл меньшего размера. - person John Bollinger; 13.10.2014