我的内存区域可以被认为是“位数组”。它们相当于
unsigned char arr[256];
但最好将其视为
bit arr[2048];
我用
访问它的单独位#define GETBIT(x,in) ((in)[ ((x)/8) ] & 1<<(7-((x)%8)))
但是我在代码的许多地方做了很多,通常是在性能关键的部分,我想知道是否有更聪明,更优化的方法。
额外信息:架构:ARM9(32位); GCC / Linux操作系统。无法更改物理数据表示 - 从外部提供或映射以供外部使用。
答案 0 :(得分:7)
我不这么认为。实际上,许多CPU架构都不会单独访问位。
在C ++上你有std::bitset<N>
。 但可能没有最高性能,具体取决于编译器的实现和优化。
顺便说一下,最好将您的位数组分组为uint32_t[32]
(或uint64_t[16]
)以进行对齐解除引用(bitset
已为您执行此操作)。
答案 1 :(得分:6)
对于随机访问单个位,您建议的宏与您将获得的一样好(只要您在编译器中启用优化)。
如果您正在访问的位有任何模式,那么您可以做得更好。例如,如果您经常访问对位,那么通过提供一个获取两位而不是一位的方法,您可能会看到一些改进,即使您并不总是最终使用这两位。 / p>
与任何优化问题一样,您需要非常熟悉代码的行为,特别是其位数组中的访问模式,以便在性能方面做出有意义的改进。
更新:由于您可以访问位范围,因此您可以从宏中获得更多性能。例如,如果您需要访问四位,则可能具有以下宏:
#define GETBITS_0_4(x,in) (((in)[(x)/8] & 0x0f))
#define GETBITS_1_4(x,in) (((in)[(x)/8] & 0x1e) >> 1)
#define GETBITS_2_4(x,in) (((in)[(x)/8] & 0x3c) >> 2)
#define GETBITS_3_4(x,in) (((in)[(x)/8] & 0x78) >> 3)
#define GETBITS_4_4(x,in) (((in)[(x)/8] & 0xf0) >> 4)
#define GETBITS_5_4(x,in) ((((in)[(x)/8] & 0xe0) >> 5) | (((in)[(x)/8+1] & 0x01)) << 3)
#define GETBITS_6_4(x,in) ((((in)[(x)/8] & 0xc0) >> 6) | (((in)[(x)/8+1] & 0x03)) << 2)
#define GETBITS_7_4(x,in) ((((in)[(x)/8] & 0x80) >> 7) | (((in)[(x)/8+1] & 0x07)) << 1)
// ...etc
这些宏会从每个位位置0,1,2等中删除4位。(为了减少无意义括号的扩散,您可能希望使用内联函数来实现上述。)然后可能定义内联函数功能如:
inline int GETBITS_4(int x, unsigned char *in) {
switch (x % 8) {
case 0: return GETBITS_0_4(x,in);
case 1: return GETBITS_1_4(x,in);
case 2: return GETBITS_2_4(x,in);
// ...etc
}
}
由于这是一个繁琐的样板代码,特别是如果你有多个不同的宽度,你可能想编写一个程序来生成所有GETBIT_*
访问器函数。
(我注意到你的字节中的位以与我上面写的相反的顺序存储。如果需要,应用适当的转换来匹配你的结构。)
答案 2 :(得分:3)
以格雷格的解决方案为基础:
template<unsigned int n, unsigned int m>
inline unsigned long getbits(unsigned long[] bits) {
const unsigned bitsPerLong = sizeof(unsigned long) * CHAR_BIT
const unsigned int bitsToGet = m - n;
BOOST_STATIC_ASSERT(bitsToGet < bitsPerLong);
const unsigned mask = (1UL << bitsToGet) - 1;
const size_t index0 = n / bitsPerLong;
const size_t index1 = m / bitsPerLong;
// Do the bits to extract straddle a boundary?
if (index0 == index1) {
return (bits[index0] >> (n % bitsPerLong)) & mask;
} else {
return ((bits[index0] >> (n % bitsPerLong)) + (bits[index1] << (bitsPerLong - (m % bitsPerLong)))) & mask;
}
}
即使它们没有对齐,也可以获得至少32位。请注意,故意inline
,因为您不希望拥有大量这些功能。
答案 3 :(得分:1)
如果您反转'array'中的位顺序,则可以消除宏中的减法。这是我能说的最好的,不知道问题的上下文(如何使用这些位)。
答案 4 :(得分:1)
#define GETBIT(x,in) ((in)[ ((x)/8) ] & 1<<(7-((x)%8)))
可以进行优化。
1)使用标准int,它通常是最快的可访问整数数据类型。 如果您不需要可移植,可以找出int的大小 sizeof并调整以下代码。
2)
#define GETBIT(x,in) ((in)[ ((x) >>> 3) ] & 1<<((x) & 7))
mod运算符%比ANDing慢。而且你不需要减去, 只需调整SETBIT例程即可。
答案 5 :(得分:0)
为什么不创建自己的包装类?
然后,您可以使用+等运算符向“数组”添加位,并使用[]运算符返回各个位。
使用&amp;可以改善您的宏7而不是%8,但编译器可能会为您进行优化。
我最近做了你正在做的事情,我的流可以包含任意数量的比特。
所以我有以下内容:
BitStream< 1 > oneBitBitStream;
BitStream< 2 > twoBitBitStream;
oneBitBitStream += Bit_One;
oneBitBitStream += Bit_Zero;
twoBitBitStream += Bit_Three;
twoBitBitStream += Bit_One;
等等。它提供了良好的可读代码,您可以为它提供类似STL的接口以帮助相似性:)
答案 6 :(得分:0)
由于问题是用C ++标记的,您有什么理由不能简单地使用标准bitset吗?
答案 7 :(得分:0)
您可以使用std::vector<bool>
代替unsigned char数组和自定义宏。向量类模板具有bool类型的特殊模板特化。这个专门化是为了优化空间分配:在这个模板专门化中,每个元素只占一位(比C ++中最小的类型小八倍:char)。