unsigned char *data
指针,并且我想检查该指针处的size_t length
数据是否为空,那么最快的方法是什么?换句话说,确认内存区域是否为空的最快方式是什么?我正在iOS中实现,因此您可以假设iOS框架可用,如果有帮助的话,简单的C方法(例如
memcmp
之类)也可以。请注意,我不尝试清除内存,而是尝试确认它已经清除(如果这有帮助的话,我正在尝试找出位图数据中是否有任何东西)。例如,我认为以下内容会起作用,尽管我还没有尝试过:
- BOOL data:(unsigned char *)data isNullToLength:(size_t)length {
unsigned char tester[length] = {};
memset(tester, 0, length);
if (memcmp(tester, data, length) != 0) {
return NO;
}
return YES;
}
虽然源数据可能非常大,我宁愿不创建一个测试数组,因为我想避免为测试分配内存,即使是暂时的。但是我可能有点太保守了。
更新:一些测试
感谢所有在下面提供出色回答的人。我决定创建一个测试应用程序来查看这些算法的性能,答案让我很惊讶,所以我想分享一下。首先,我将展示我使用的算法版本(在某些情况下,它们与提议的算法稍有不同),然后我将分享一些现场结果。
测试
首先,我创建了一些示例数据:
size_t length = 1024 * 768;
unsigned char *data = (unsigned char *)calloc(sizeof(unsigned char), (unsigned long)length);
int i;
int count;
long check;
int loop = 5000;
每个测试都包括一个循环运行 loop
次。在循环期间,随机数据被添加到和从 data
字节流中删除。注意,有一半的时间实际上没有添加任何数据,所以有一半的时间测试不应该找到任何非零数据。请注意,testZeros
调用是对下面测试例程调用的占位符。计时器在循环前启动并在循环后停止。
count = 0;
for (i=0; i<loop; i++) {
int r = random() % length;
if (random() % 2) { data[r] = 1; }
if (! testZeros(data, length)) {
count++;
}
data[r] = 0;
}
测试 A: nullToLength。这更或多或少是我原先上面的公式,经过调试和简化。
- (BOOL)data:(void *)data isNullToLength:(size_t)length {
void *tester = (void *)calloc(sizeof(void), (unsigned long)length);
int test = memcmp(tester, data, length);
free(tester);
return (! test);
}
测试 B:allZero。由 Carrotman 提出。
BOOL allZero (unsigned char *data, size_t length) {
bool allZero = true;
for (int i = 0; i < length; i++){
if (*data++){
allZero = false;
break;
}
}
return allZero;
}
测试 C:is_all_zero。由 Lundin 提出。
BOOL is_all_zero (unsigned char *data, size_t length)
{
BOOL result = TRUE;
unsigned char* end = data + length;
unsigned char* i;
for(i=data; i<end; i++) {
if(*i > 0) {
result = FALSE;
break;
}
}
return result;
}
测试D:sumArray。这是来自几乎重复的问题的最佳答案,由vladr提出。
BOOL sumArray (unsigned char *data, size_t length) {
int sum = 0;
for (int i = 0; i < length; ++i) {
sum |= data[i];
}
return (sum == 0);
}
测试E:lulz。由Steve Jessop提出。
BOOL lulz (unsigned char *data, size_t length) {
if (length == 0) return 1;
if (*data) return 0;
return memcmp(data, data+1, length-1) == 0;
}
测试 F:NSData。这是一个我在处理所有这些时在 iOS SDK 中发现的使用 NSData 对象的测试。结果证明,苹果确实有一种比较字节流的方法,旨在与硬件无关。
- (BOOL)nsdTestData: (NSData *)nsdData length: (NSUInteger)length {
void *tester = (void *)calloc(sizeof(void), (unsigned long)length);
NSData *nsdTester = [NSData dataWithBytesNoCopy:tester length:(NSUInteger)length freeWhenDone:NO];
int test = [nsdData isEqualToData:nsdTester];
free(tester);
return (test);
}
结果
那么这些方法的比较如何?以下是两组数据,每组数据表示通过检查5000次。首先我在相对陈旧的iMac上运行iPhone模拟器,然后我在第一代iPad上运行。
在运行iPhone 4.3模拟器的iMac上:
// Test A, nullToLength: 0.727 seconds
// Test F, NSData: 0.727
// Test E, lulz: 0.735
// Test C, is_all_zero: 7.340
// Test B, allZero: 8.736
// Test D, sumArray: 13.995
在第一代iPad上:
// Test A, nullToLength: 21.770 seconds
// Test F, NSData: 22.184
// Test E, lulz: 26.036
// Test C, is_all_zero: 54.747
// Test B, allZero: 63.185
// Test D, sumArray: 84.014
这只是两个样本,我运行了很多次测试,结果略有不同。性能的顺序总是相同的:A&F非常接近,E稍微落后,C、B和D。我会说A、F和E是虚拟并列的,在iOS上我更喜欢F,因为它利用了苹果对处理器更改问题的保护,但A和E也非常接近。从memcmp方法显然比简单的循环方法胜出,在模拟器上快近十倍,设备本身上快两倍。奇怪的是,在另一篇帖子中获胜的答案D在这个测试中表现非常差,可能是因为当它遇到第一个不同之处时没有跳出循环。