我有大量数据,可能有 4MB。现在想检查其中的所有位是否都为 0。
例如:
这是数据:
void* data = malloc(4*1024*1024);
memset(data, 0, 4*1024*1024);
检查其中的所有位是否都为 0。这是我的解决方案,但速度不够快:
int dataisnull(char* data, int length)
{
int i = 0;
while(i<length){
if (data[i]) return 0;
i++;
}
return 1;
}
此代码可能有一些性能需要改进的地方。例如,在 32/64 位机器中,一次检查 4/8 个字节可能会更快。
所以我想知道最快的方法是什么?
您可以一次处理多个字节并展开循环:
int dataisnull(const void *data, size_t length) {
/* assuming data was returned by malloc, thus is properly aligned */
size_t i = 0, n = length / sizeof(size_t);
const size_t *pw = data;
const unsigned char *pb = data;
size_t val;
#define UNROLL_FACTOR 8
#if UNROLL_FACTOR == 8
size_t n1 = n - n % UNROLL_FACTOR;
for (; i < n1; i += UNROLL_FACTOR) {
val = pw[i + 0] | pw[i + 1] | pw[i + 2] | pw[i + 3] |
pw[i + 4] | pw[i + 5] | pw[i + 6] | pw[i + 7];
if (val)
return 0;
}
#endif
val = 0;
for (; i < n; i++) {
val |= pw[i];
}
for (i = n * sizeof(size_t); i < length; i++) {
val |= pb[i];
}
return val == 0;
}
根据您的具体问题,提前或延迟检测非零值可能更有效:
- 如果全零情况最常见,您应该计算将所有位累加到
val
累加器中并仅在最后测试。
- 如果全零的情况很少见,您应该更频繁地检查非零值。
上面的展开版本是一个折衷方案,它根据 size_t
的大小每 64 或 128 字节测试一次非零值。
根据您的编译器和处理器,您可能会通过更少或更多展开来获得更好的性能。您也可以使用适用于您的特定体系结构的内部函数来利用 vector 类型,但它的可移植性较差。
请注意,代码不会验证 data
指针是否正确对齐:
- 它不能移植。
- 它假定数据是通过
malloc
或类似方式分配的,因此可以正确对齐任何类型。
一如既往,对不同的解决方案进行基准测试,看看它是否会产生真正的影响。这个功能可能根本不是瓶颈,编写一个复杂的功能来优化罕见的情况会适得其反,它会降低代码的可读性,更有可能包含错误并且更难维护。例如,如果您更改内存分配方案或使用静态数组,数据对齐假设可能不成立,那么该函数可能会调用未定义的行为。