你能改变你的编码吗?正如您所发现的,在每个字节上使用一个位来指示是否有另一个字节在处理效率方面确实很糟糕。
更好的方法是对 UTF-8 建模,它将完整 int 的长度编码为第一个字节:
0xxxxxxx // one byte with 7 bits of data
10xxxxxx 10xxxxxx // two bytes with 12 bits of data
110xxxxx 10xxxxxx 10xxxxxx // three bytes with 16 bits of data
1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx // four bytes with 22 bits of data
// etc.
但是 UTF-8 有一些特殊的属性,使它更容易与 ASCII 区分开来。这会使数据膨胀,并且您不关心 ASCII,因此您可以将其修改为如下所示:
0xxxxxxx // one byte with 7 bits of data
10xxxxxx xxxxxxxx // two bytes with 14 bits of data.
110xxxxx xxxxxxxx xxxxxxxx // three bytes with 21 bits of data
1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx // four bytes with 28 bits of data
// etc.
这与您的方法具有相同的压缩级别(最多 64 位 = 9 字节),但 CPU 处理起来要容易得多。
从这里你可以为第一个字节建立一个查找表,它给你一个掩码和长度:
// byte_counts[255] contains the number of additional
// bytes if the first byte has a value of 255.
uint8_t const byte_counts[256]; // a global constant.
// byte_masks[255] contains a mask for the useful bits in
// the first byte, if the first byte has a value of 255.
uint8_t const byte_masks[256]; // a global constant.
然后解码:
// the resulting value.
uint64_t v = 0;
// mask off the data bits in the first byte.
v = *data & byte_masks[*data];
// read in the rest.
switch(byte_counts[*data])
{
case 3: v = v << 8 | *++data;
case 2: v = v << 8 | *++data;
case 1: v = v << 8 | *++data;
case 0: return v;
default:
// If you're on VC++, this'll make it take one less branch.
// Better make sure you've got all the valid inputs covered, though!
__assume(0);
}
无论整数大小如何,它都只命中一个分支点:开关,它很可能会被放入一个跳转表中。通过不让每个案例都失败,您可以为 ILP 进一步优化它。