suppose have nsdictionary 2 sub collections of nsarray , nsdictionary:
nsmutabledictionary *mkdict(void){ nsmutabledictionary *dict=[nsmutabledictionary dictionary]; nsmutabledictionary *sub=[nsmutabledictionary dictionary]; nsmutablearray *array= [nsmutablearray array]; [dict setobject:array forkey:@"array_key"]; [dict setobject:sub forkey:@"dict_key"]; return dict; }
there multitude of ways access single element of sub-collection, , choose time 3 of them.
the first way indirectly access subelements accessing key of parent:
void kvc1(nsmutabledictionary *dict, int count){ for(int i=0; i<count; i++){ char buf1[40], buf2[sizeof buf1]; snprintf(buf1,sizeof(buf1),"element %i", i); snprintf(buf2, sizeof buf2, "key %i", i); [[dict objectforkey:@"array_key"] addobject: [nsstring stringwithutf8string:buf1]]; [[dict objectforkey:@"dict_key"] setobject:[nsstring stringwithutf8string:buf1] forkey:[nsstring stringwithutf8string:buf2]]; } }
the second use keypath access:
void kvc2(nsmutabledictionary *dict, int count){ for(int i=0; i<count; i++){ char buf1[40], buf2[sizeof buf1], buf3[sizeof buf1]; snprintf(buf1,sizeof(buf1),"element %i", i); snprintf(buf2, sizeof buf2, "key %i", i); snprintf(buf3, sizeof buf3, "dict_key.key %i",i); [dict insertvalue: [nsstring stringwithutf8string:buf1] atindex:i inpropertywithkey:@"array_key"]; [dict setvalue: [nsstring stringwithutf8string:buf1] forkeypath: [nsstring stringwithutf8string:buf3]]; } }
and third, similar first, access pointer sub element, use pointer:
void kvc3(nsmutabledictionary *dict, int count){ nsmutablearray *subarray = [dict objectforkey:@"array_key"]; nsmutabledictionary *subdict = [dict objectforkey:@"dict_key"]; for(int i=0; i<count; i++){ char buf1[40], buf2[sizeof buf1]; snprintf(buf1,sizeof(buf1),"element %i", i); snprintf(buf2, sizeof buf2, "key %i", i); [subarray addobject:[nsstring stringwithutf8string:buf1]]; [subdict setobject: [nsstring stringwithutf8string:buf1] forkey: [nsstring stringwithutf8string:buf2]]; } }
here timing code:
#import <foundation/foundation.h> #import <mach/mach_time.h> // kvc1, kvc2 , kvc3 above... #define time_this(func,times) \ ({\ mach_timebase_info_data_t info; \ mach_timebase_info(&info); \ uint64_t start = mach_absolute_time(); \ for(int i=0; i<(int)times; i++) \ func ; \ uint64_t duration = mach_absolute_time() - start; \ duration *= info.numer; \ duration /= info.denom; \ duration /= 1000000; \ nslog(@"%i executions of line %i took %lld milliseconds", times, __line__, duration); \ }); int main (int argc, const char * argv[]) { nsautoreleasepool * pool = [[nsautoreleasepool alloc] init]; nsmutabledictionary *dict=mkdict(); nsmutabledictionary *dict2=mkdict(); nsmutabledictionary *dict3=mkdict(); time_this(kvc1(dict,1000),10); time_this(kvc2(dict2,1000),10); time_this(kvc3(dict3,1000),10); if([dict isequaltodictionary:dict2]) nslog(@"and same..."); [pool drain]; return 0; }
here results:
10 executions of line 256 took 57 milliseconds 10 executions of line 257 took 7930 milliseconds 10 executions of line 258 took 46 milliseconds , same...
question: why os x snow leopard / lion suggested method of using keypaths stinking slow? if increase size of count
10,000 or more, kvc2 becomes infinitely slow other 2 methods increase linearly.
am doing wrong? there better idiom access single element of sub collection in dictionary?
in kvc2()
, send
[dict insertvalue:[nsstring stringwithutf8string:buf1] atindex:i inpropertywithkey:@"array_key"];
the documentation method states following:
the method
insertin<key>:atindex:
invoked if exists. if no corresponding scripting-kvc-compliant method (insertin<key>:atindex:
) found, method invokesmutablearrayvalueforkey:
, mutates result.
since message being sent dict
, instance of nsdictionary
, there no -insertin<key>:atindex:
method, hence -mutablearrayvalueforkey:
sent. documentation method states following:
return value mutable array proxy provides read-write access ordered to-many relationship specified key.
discussion objects added mutable array become related receiver, , objects removed mutable array become unrelated. default implementation recognizes same simple accessor methods , array accessor methods valueforkey:, , follows same direct instance variable access policies, returns mutable collection proxy object instead of immutable collection valueforkey: return.
so what’s happening at each iteration:
- a proxy mutable array created mutable copy of original array;
- an object added proxy array;
- the proxy array adds same object original array.
if use instruments profile program, you’ll notice 50% of processing time spent on -[nskeyvalueslowmutablearray insertobject:atindex:]
— think it’s safe assume nskeyvalueslowmutablearray
proxy array , name should clue of performance.
Comments
Post a Comment