在 C++ 中向量化位打包
Vectorizing bits packing in C++
我正在编写一个工具,用于对 6 个不同字母(例如 >1000000 个字母)的长字符串进行操作,因此我想将每个字母编码为少于 8 位(对于 6 个字母,3 位就足够了)
这是我的代码:
Rcpp::RawVector pack(Rcpp::RawVector UNPACKED,
const unsigned short ALPH_SIZE) {
const unsigned int IN_LEN = UNPACKED.size();
Rcpp::RawVector ret((ALPH_SIZE * IN_LEN + BYTE_SIZE - 1) / BYTE_SIZE);
unsigned int out_byte = ZERO;
unsigned short bits_left = BYTE_SIZE;
for (int i = ZERO; i < IN_LEN; i++) {
if (bits_left >= ALPH_SIZE) {
ret[out_byte] |= (UNPACKED[i] << (bits_left - ALPH_SIZE));
bits_left -= ALPH_SIZE;
} else {
ret[out_byte] |= (UNPACKED[i] >> (ALPH_SIZE - bits_left));
bits_left = ALPH_SIZE - bits_left;
out_byte++;
ret[out_byte] |= (UNPACKED[i] << (BYTE_SIZE - bits_left));
bits_left = BYTE_SIZE - bits_left;
}
}
return ret;
}
我正在使用 Rcpp,它是 C++ 的 R 接口。 RawVector
实际上是 char
中的 vector
。
这段代码工作得很好——只是它太慢了。我正在一点一点地执行操作,同时我可以以某种方式对其进行矢量化。这里有一个问题——是否有任何库或工具可以做到这一点? C++ 工具不认可我。
提前致谢!
This code works just perfectly - except it is too slow.
那么您可能想试试 4-bits/letter。用 space 换取时间。如果 4 位满足您的压缩需求(仅大 33.3%),那么您的代码可以处理半字节,这将比 tri-bits.
更快更简单
您需要展开循环,以便优化器可以从中获得有用的东西。它还将摆脱你的 if
,这会扼杀任何快速表现的机会。像这样:
int i = 0;
for(i = 0; i + 8 <= IN_LEN; i += 8) {
ret[out_byte ] = (UNPACKED[i] ) | (UNPACKED[i + 1] << 3) | (UNPACKED[i + 2] << 6);
ret[out_byte + 1] = (UNPACKED[i + 2] >> 2) | (UNPACKED[i + 3] << 1) | (UNPACKED[i + 4] << 4) | (UNPACKED[i + 5] << 7);
ret[out_byte + 2] = (UNPACKED[i + 5] >> 1) | (UNPACKED[i + 6] << 2) | (UNPACKED[i + 7] << 5);
out_byte += 3;
}
for (; i < IN_LEN; i++) {
if (bits_left >= ALPH_SIZE) {
ret[out_byte] |= (UNPACKED[i] << (bits_left - ALPH_SIZE));
bits_left -= ALPH_SIZE;
} else {
ret[out_byte] |= (UNPACKED[i] >> (ALPH_SIZE - bits_left));
bits_left = ALPH_SIZE - bits_left;
out_byte++;
ret[out_byte] |= (UNPACKED[i] << (BYTE_SIZE - bits_left));
bits_left = BYTE_SIZE - bits_left;
}
}
这将允许优化器对整个事物进行矢量化(假设它足够聪明)。对于您当前的实现,我怀疑任何当前的编译器都能发现您的代码在 3 个写入字节后循环并滥用它。
编辑:
有了足够的 constexpr / template 魔法,您也许可以为循环体编写一些通用处理程序。或者只覆盖所有小值(比如为从 1 到 16 的每个位计数编写专门的模板函数)。在 16 位之后按位打包值是多余的。
我正在编写一个工具,用于对 6 个不同字母(例如 >1000000 个字母)的长字符串进行操作,因此我想将每个字母编码为少于 8 位(对于 6 个字母,3 位就足够了)
这是我的代码:
Rcpp::RawVector pack(Rcpp::RawVector UNPACKED,
const unsigned short ALPH_SIZE) {
const unsigned int IN_LEN = UNPACKED.size();
Rcpp::RawVector ret((ALPH_SIZE * IN_LEN + BYTE_SIZE - 1) / BYTE_SIZE);
unsigned int out_byte = ZERO;
unsigned short bits_left = BYTE_SIZE;
for (int i = ZERO; i < IN_LEN; i++) {
if (bits_left >= ALPH_SIZE) {
ret[out_byte] |= (UNPACKED[i] << (bits_left - ALPH_SIZE));
bits_left -= ALPH_SIZE;
} else {
ret[out_byte] |= (UNPACKED[i] >> (ALPH_SIZE - bits_left));
bits_left = ALPH_SIZE - bits_left;
out_byte++;
ret[out_byte] |= (UNPACKED[i] << (BYTE_SIZE - bits_left));
bits_left = BYTE_SIZE - bits_left;
}
}
return ret;
}
我正在使用 Rcpp,它是 C++ 的 R 接口。 RawVector
实际上是 char
中的 vector
。
这段代码工作得很好——只是它太慢了。我正在一点一点地执行操作,同时我可以以某种方式对其进行矢量化。这里有一个问题——是否有任何库或工具可以做到这一点? C++ 工具不认可我。
提前致谢!
This code works just perfectly - except it is too slow.
那么您可能想试试 4-bits/letter。用 space 换取时间。如果 4 位满足您的压缩需求(仅大 33.3%),那么您的代码可以处理半字节,这将比 tri-bits.
更快更简单您需要展开循环,以便优化器可以从中获得有用的东西。它还将摆脱你的 if
,这会扼杀任何快速表现的机会。像这样:
int i = 0;
for(i = 0; i + 8 <= IN_LEN; i += 8) {
ret[out_byte ] = (UNPACKED[i] ) | (UNPACKED[i + 1] << 3) | (UNPACKED[i + 2] << 6);
ret[out_byte + 1] = (UNPACKED[i + 2] >> 2) | (UNPACKED[i + 3] << 1) | (UNPACKED[i + 4] << 4) | (UNPACKED[i + 5] << 7);
ret[out_byte + 2] = (UNPACKED[i + 5] >> 1) | (UNPACKED[i + 6] << 2) | (UNPACKED[i + 7] << 5);
out_byte += 3;
}
for (; i < IN_LEN; i++) {
if (bits_left >= ALPH_SIZE) {
ret[out_byte] |= (UNPACKED[i] << (bits_left - ALPH_SIZE));
bits_left -= ALPH_SIZE;
} else {
ret[out_byte] |= (UNPACKED[i] >> (ALPH_SIZE - bits_left));
bits_left = ALPH_SIZE - bits_left;
out_byte++;
ret[out_byte] |= (UNPACKED[i] << (BYTE_SIZE - bits_left));
bits_left = BYTE_SIZE - bits_left;
}
}
这将允许优化器对整个事物进行矢量化(假设它足够聪明)。对于您当前的实现,我怀疑任何当前的编译器都能发现您的代码在 3 个写入字节后循环并滥用它。
编辑: 有了足够的 constexpr / template 魔法,您也许可以为循环体编写一些通用处理程序。或者只覆盖所有小值(比如为从 1 到 16 的每个位计数编写专门的模板函数)。在 16 位之后按位打包值是多余的。