Если у меня есть указатель unsigned char *data
и я хочу проверить, является ли size_t length
данных в этом указателе NULL, что будет самым быстрым способом сделать это?Другими словами, какой самый быстрый способ убедиться, что область памяти пуста?
Я внедряю в 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.Предложено Лундином.
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.Предложено Стивом Джессопом.
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.Это тест с использованием объекта NSData, который я обнаружил в iOS SDK, работая над всем этим.Оказывается, у Apple есть идея о том, как сравнивать потоки байтов, разработанные как независимые от аппаратного обеспечения.
- (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 циклов проверки.Сначала я попробовал это на iPhone Simulator, работающем на относительно старом iMac, затем я попробовал это на iPad первого поколения.
На iPhone 4.3 Simulator, работающем на 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, потому что этоиспользует защиту Apple от проблем с заменой процессора, но A & E очень близки.Подход с использованием memcmp явно выигрывает у простого подхода с использованием петель, почти в десять раз быстрее в симуляторе и в два раза быстрее на самом устройстве.Как ни странно, D, победивший ответ из другого потока показал себя очень плохо в этом тесте, возможно, потому, что он не вырвался из цикла при достижении первого различия.