Это самый оптимальный способ? Си битовые поля
Я сделал функцию, чтобы установить или очистить определенное количество битов в DWORD. Моя функция работает. Мне не нужна помощь, чтобы это сработало. Тем не менее, мне интересно, если метод, который я выбрал, это самый быстрый способ.
Мне довольно сложно объяснить, как это работает. Есть два массива, содержащих DWORD, которые заполнены битами слева и справа от DWORD (со всеми двоичными единицами). Он создает маску со всеми заполненными битами, за исключением тех, которые я хочу установить или очистить, а затем устанавливает их с помощью побитовых операторов на основе этой маски. Это кажется довольно сложным для такой простой задачи, но, похоже, самый быстрый способ, которым я мог придумать. Это гораздо быстрее, чем устанавливать их постепенно.
static DWORD __dwFilledBitsRight[] = {
0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
};
static DWORD __dwFilledBitsLeft[] = {
0x0, 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0,
0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF
};
// nStartBitFromLeft must be between 1 and 32...
// 1 is the bit farthest to the left (actual bit 31)
// 32 is the bit farthest to the right (actual bit 0)
inline void __FillDWORDBits(DWORD *p, int nStartBitFromLeft, int nBits, BOOL bSet)
{
DWORD dwLeftMask = __dwFilledBitsLeft[nStartBitFromLeft - 1]; // Mask for data on the left of the bits we want
DWORD dwRightMask = __dwFilledBitsRight[33 - (nStartBitFromLeft + nBits)]; // Mask for data on the right of the bits we want
DWORD dwBitMask = ~(dwLeftMask | dwRightMask); // Mask for the bits we want
DWORD dwOriginal = *p;
if(bSet) *p = (dwOriginal & dwLeftMask) | (dwOriginal & dwRightMask) | (0xFFFFFFFF & dwBitMask);
else *p = (dwOriginal & dwLeftMask) | (dwOriginal & dwRightMask) | 0;
}
2 ответа
Как насчет:
// Create mask of correct length, and shift to the correct position
DWORD mask = ((1ULL << nBits) - 1) << pos;
// Apply mask (or its inverse)
if (bSet)
{
*p |= mask;
}
else
{
*p &= ~mask;
}
Вполне вероятно, что простые побитовые операции будут быстрее, чем поиск таблиц на любом современном процессоре.
Примечание: в зависимости от отношений между DWORD
а также long long
на этой платформе вам может потребоваться специальная обработка для случая, когда nBits == sizeof(DWORD)*8
, Или если nBits==0
это не возможность, вы могли бы просто сделать DWORD mask = ((2ULL << (nBits - 1)) - 1) << pos;
,
Обновление: упоминалось, что if
потенциально может быть медленным, что правда. Вот замена для этого, но вам нужно измерить, чтобы увидеть, если это на самом деле быстрее на практике.
// A bit hacky, but the aim is to get 0x00000000 or 0xFFFFFFFF
// (relies on two's-complement representation)
DWORD blanket = bSet - 1;
// Use the blanket to override one or other masking operation
*p |= (blanket | mask);
*p &= ~(blanket & mask);
Это способ, которым я бы сделал это. Я бы разбил его на две функции: setbits() и clearbits(). Для ясности выложены шаги, и я уверен, что это может быть гораздо более оптимизировано.
Эта версия зависит от 32-битного кода в его нынешнем виде. Кроме того, в моем мире бит 0 - самый правый бит. Ваш пробег может отличаться.
setbits( DWORD *p , int offset , int len )
{
// offset must be 0-31, len must be 0-31, len+offset must be 0-32
int right_shift = ( !len ? 0 : 32 - (len+offset) ) ;
int left_shift = offset ;
DWORD right_mask = 0xFFFFFFFF >> right_shift ;
DWORD left_mask = 0xFFFFFFFF << left_shift ;
DWORD mask = left_mask & right_mask ;
*p |= mask ;
return ;
}
clearbits( DWORD *p , int offset , int len )
{
// offset must be 0-31, len must be 0-31, len+offset must be 0-32
int right_shift = ( !len ? 0 : 32 - (len+offset) ) ;
int left_shift = offset ;
DWORD right_mask = 0xFFFFFFFF >> right_shift ;
DWORD left_mask = 0xFFFFFFFF << left_shift ;
DWORD mask = ~( left_mask & right_mask ) ;
*p &= mask ;
return ;
}
Я наткнулся на эту улучшенную версию, ища что-то еще сегодня. Любезность Шона Андерсона Бит Тиддлинг Хэкс в Стэнфордском университете:
// uncomment #define to get the super scalar CPU version.
// #define SUPER_SCALAR_CPU
void setbits( unsigned int *p , int offset , int len , int flag )
{
unsigned int mask = ( ( 1 << len ) - 1 ) << offset ;
#if !defined( SUPER_SCALAR_CPU )
*p ^= ( - flag ^ *p ) & mask ;
#else
// supposed to be some 16% faster on a Intel Core 2 Duo than the non-super-scalar version above
*p = (*p & ~ mask ) | ( - flag & mask ) ;
#endif
return ;
}
Однако многое зависит от вашего компилятора.