NSMutableDictionary用于巨大的浮点数据集

时间:2012-01-20 03:32:53

标签: objective-c c performance hashmap nsmutabledictionary

我有一些代码可以将大型(许多千兆字节)的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;

这个数据集使用我正在使用的XML文件消耗大约800MB的RAM,并且执行需要相当长的时间。我希望有更好的性能,但更重要的是我需要降低内存消耗,以便我可以处理更大的XML文件。

objc_msg_send就在代码的配置文件中,就像- [NSNumber numberWithInt:]一样,我确信我可以通过完全避免对象来减少内存使用量,但我对C编程知之甚少(这个项目肯定教我!)。

如何使用高效的C数据结构替换NSMuableDictionaryNSNumber MyPoint?没有任何第三方库依赖?

我还希望能够将此数据结构写入磁盘上的文件,因此我可以使用不完全适合内存的数据集,但我可能没有这种功能。

(对于不熟悉Objective-C的人,NSMutableDictionary类只能存储Obj-C对象,键也必须是对象.NSNumber和MyPoint是哑容器类,允许NSMutableDictionary与float一起工作和int值。)

修改

我已尝试使用CFMutableDictionary存储结构,按apple's sample code。当字典为空时,它表现很好。但随着字典的增长,它变得越来越慢。大约25%通过解析文件(字典中大约400万个项目),它开始变形,比文件中的早期慢两个数量级。

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);
}

编辑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 个答案:

答案 0 :(得分:4)

如果您想要类似NSMutableDictionary的行为但使用malloc内存,则可以下拉到CFDictionary(或者在您的情况下,CFMutableDictionary)。它实际上是NSMutableDictionary的基础,但它允许一些自定义,即你可以告诉它你没有存储对象。当你调用CFDictionaryCreateMutable()时,你给它一个结构来描述你正在处理它的值(它包含指示它如何保留,释放,描述,散列和比较你的值的指针)。因此,如果你想使用一个包含两个浮点数的结构,并且你很高兴为每个结构使用malloc内存,你可以对你的结构进行malloc,填充它,并将其交给CFDictionary,然后就可以了编写回调函数,使它们与您的特定结构一起使用。您可以CFDictionary使用的键和对象的唯一限制是它们需要适合void *

答案 1 :(得分:3)

对于这类事情,我只会使用C ++容器std::unordered_mapstd::pair。您可以在Objective-C ++中使用它们。只需为您的文件添加.mm扩展名,而不是通常的.m扩展名。

更新

在你的评论中,你说你以前从未做过C ++。在这种情况下,您应该尝试Kevin Ballard对CFDictionary的回答,或者查看标准库中的hcreatehdestroyhsearch函数。

hcreate man page

答案 2 :(得分:0)

将.m文件重命名为.mm并切换到使用C ++:

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