NSMutableDictionary для огромного набора данных с плавающей точкой - PullRequest
3 голосов
/ 20 января 2012

У меня есть код для преобразования большого (много гигабайт) XML-файла в другой формат.

Помимо прочего, мне нужно хранить один или два гигабайта с плавающей точкой в ​​хэш-таблице (два с плавающей точкой)для каждой записи), с int в качестве ключа значения.

В настоящее время я использую NSMutableDictionary и пользовательский класс, содержащий два числа с плавающей запятой:

// create the dictionary
NSMutableDictionary *points = [[NSMutableDictionary alloc] init];

// add an entry (the data is read from an XML file using libxml)
int pointId = 213453;
float x = 42.313554; 
float y = -21.135213; 

MyPoint *point = [[MyPoint alloc] initWithX:x Y:y];
[points setObject:point forKey:[NSNumber numberWithInt:pointId]];
[point release];

// retrieve an entry (this happens later on while parsing the same XML file)
int pointId = 213453;
float x;
float y;
MyPoint *point = [points objectForKey:[NSNumber numberWithInt:pointId]];
x = point.x;
y = point.y;

Этот набор данных потребляет около800 МБ ОЗУ с файлом XML, с которым я сейчас работаю, и на его выполнение уходит довольно много времени.Я хотел бы иметь более высокую производительность, но еще более важно, чтобы мне потребовалось уменьшить потребление памяти, чтобы я мог обрабатывать еще большие XML-файлы.

objc_msg_send прямо в профиле кода, как и- [NSNumber numberWithInt:], и я уверен, что смогу уменьшить использование памяти, вообще избегая объектов, но я не очень разбираюсь в программировании на C (этот проект, безусловно, учит меня!).

Как я могузаменить NSMuableDictionary, NSNumber MyPoint на эффективную структуру данных C?Без каких-либо сторонних библиотечных зависимостей?

Я также хотел бы иметь возможность записать эту структуру данных в файлы на диске, чтобы я мог работать с набором данных, который не полностью помещается в память, но явероятно, может жить без этой возможности.

(для тех, кто не знаком с Objective-C, класс NSMutableDictionary может хранить только объекты Obj-C, и ключи также должны быть объектами. NSNumber и MyPointклассы тупого контейнера, позволяющие NSMutableDictionary работать со значениями float и int.)

РЕДАКТИРОВАТЬ:

Я пытался использовать CFMutableDictionaryхранить структуры согласно образцу кода яблока .Когда словарь пуст, он работает отлично.Но когда словарь растет, он становится все медленнее и медленнее.Примерно на 25% при разборе файла (~ 4 миллиона элементов в словаре) он начинает пыхтеть, на два порядка медленнее, чем раньше в файле.

NSMutableDictionary не имеет той же проблемы с производительностью.Инструменты демонстрируют большую активность, применяя хеши и сравнивая ключи словаря (метод intEqual() ниже).Сравнение int быстрое, поэтому что-то очень неправильно для его выполнения так часто.

Вот мой код для создания словаря:

typedef struct {
  float lat;
  float lon;
} AGPrimitiveCoord;

void agPrimitveCoordRelease(CFAllocatorRef allocator, const void *ptr) {
    CFAllocatorDeallocate(allocator, (AGPrimitiveCoord *)ptr);
}

Boolean agPrimitveCoordEqual(const void *ptr1, const void *ptr2) {
    AGPrimitiveCoord *p1 = (AGPrimitiveCoord *)ptr1;
    AGPrimitiveCoord *p2 = (AGPrimitiveCoord *)ptr2;

    return (fabsf(p1->lat - p2->lat) < 0.0000001 && fabsf(p1->lon - p2->lon) < 0.0000001);

}

Boolean intEqual(const void *ptr1, const void *ptr2) {
    return (int)ptr1 == (int)ptr2;
}

CFHashCode intHash(const void *ptr) {
  return (CFHashCode)((int)ptr);
}

// init storage dictionary
CFDictionaryKeyCallBacks intKeyCallBacks = {0, NULL, NULL, NULL, intEqual, intHash};
CFDictionaryValueCallBacks agPrimitveCoordValueCallBacks = {0, NULL /*agPrimitveCoordRetain*/, agPrimitveCoordRelease, NULL, agPrimitveCoordEqual};
temporaryNodeStore = CFDictionaryCreateMutable(NULL, 0, &intKeyCallBacks, &agPrimitveCoordValueCallBacks);

// add an item to the dictionary
- (void)parserRecordNode:(int)nodeId lat:(float)lat lon:(float)lon
{
  AGPrimitiveCoord *coordPtr = (AGPrimitiveCoord *)CFAllocatorAllocate(NULL, sizeof(AGPrimitiveCoord), 0);
  coordPtr->lat = lat;
  coordPtr->lon = lon;

  CFDictionarySetValue(temporaryNodeStore, (void *)nodeId, coordPtr);
}

EDIT 2:

Проблема с производительностью была вызвана практически бесполезной реализацией хеширования в примере кода Apple.Я поднял производительность, используя это:

// hash algorithm from http://burtleburtle.net/bob/hash/integer.html
uint32_t a = abs((int)ptr);
a = (a+0x7ed55d16) + (a<<12);
a = (a^0xc761c23c) ^ (a>>19);
a = (a+0x165667b1) + (a<<5);
a = (a+0xd3a2646c) ^ (a<<9);
a = (a+0xfd7046c5) + (a<<3);
a = (a^0xb55a4f09) ^ (a>>16);

Ответы [ 3 ]

4 голосов
/ 20 января 2012

Если вы хотите поведение, подобное NSMutableDictionary, но с памятью malloc, вы можете перейти к CFDictionary (или, в вашем случае, CFMutableDictionary ).На самом деле это основа NSMutableDictionary, но он допускает некоторую настройку, а именно, вы можете сказать ему, что вы не храните объекты.Когда вы вызываете CFDictionaryCreateMutable(), вы даете ему структуру, которая описывает, какие значения вы передаете (она содержит указатели, которые сообщают ему, как сохранять, освобождать, описывать, хешировать и сравнивать ваши значения).Поэтому, если вы хотите использовать структуру, содержащую два числа с плавающей запятой, и вы счастливы использовать память malloc для каждой структуры, вы можете распределить свою структуру по malloc, заполнить ее и передать ее в CFDictionary, а затем вы можете написатьфункции обратного вызова, так что они работают с вашей конкретной структурой.Единственное ограничение на ключи и объекты, с которыми вы можете использовать CFDictionary, - это то, что они должны помещаться внутри void *.

3 голосов
/ 20 января 2012

Для такого рода вещей я бы просто использовал C ++ контейнеры std::unordered_map и std::pair. Вы можете использовать их в Objective-C ++. Просто дайте вашим файлам расширение .mm вместо обычного расширения .m.

Обновление

В своем комментарии вы сказали, что никогда раньше не делали C ++. В этом случае вам следует либо попробовать ответ Кевина Балларда CFDictionary, либо проверить функции hcreate, hdestroy и hsearch в стандартной библиотеке.

hcreate справочная страница

0 голосов
/ 20 января 2012

Переименуйте ваш файл .m в .mm и переключитесь на C ++:

std::map<int, std::pair<float>> points;
...