обединяване на 32-битова структура с uint32_t и GCC атомарни инструкции

В многопоточна програма, която беше написана, имам някои проблеми с производителността с много голямо състезание за заключване.

Реших този проблем, като имах няколко флага в рамките на 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);

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