Objective-c: Проблемы с блоками и NSEnumerationConcurrent

У меня есть словарь, содержащий второй словарь с 1000 записями. Все записи представляют собой NSStrings типа key = key XXX и value = element XXX , где XXX - это число от 0 - количество элементов - 1. . (Несколько дней назад Я спросил о словарях Objective-C, содержащих словарь. Пожалуйста, обратитесь к этому вопросу , если вам нужен код, который создает словарь.)

Суммарная длина всех строк во вложенном словаре составляет 28 670 символов. т.е.:

strlen("key 0")+strlen("element 0")+
//and so on up through 
strlen("key 999")+strlen("element 999") == 28670. 

Считайте это очень простым хеш-значением как индикатором, если метод перечислил каждую пару ключ + значение один раз и только один раз.

У меня есть одна подпрограмма, которая отлично работает (с использованием блоков) для доступа к индивидуальному ключу словаря и значения:

NSUInteger KVC_access3(NSMutableDictionary *dict){
    __block NSUInteger ll=0;
    NSMutableDictionary *subDict=[dict objectForKey:@"dict_key"];

    [subDict 
        enumerateKeysAndObjectsUsingBlock:
            ^(id key, id object, BOOL *stop) {
                ll+=[object length];
                ll+=[key length];
    }];
    return ll;
}
// will correctly return the expected length...

Если я попробую то же самое, используя параллельные блоки (на многопроцессорной машине), я получу число, близкое, но не совсем ожидаемое, 28670:

NSUInteger KVC_access4(NSMutableDictionary *dict){
    __block NSUInteger ll=0;
    NSMutableDictionary *subDict=[dict objectForKey:@"dict_key"];

    [subDict 
        enumerateKeysAndObjectsWithOptions:
            NSEnumerationConcurrent
        usingBlock:
            ^(id key, id object, BOOL *stop) {
                ll+=[object length];
                ll+=[key length]; 
    }];
    return ll;
}
// will return correct value sometimes; a shortfall value most of the time...

Документы Apple для состояния NSEnumerationConcurrent :

 "the code of the Block must be safe against concurrent invocation."

Думаю, проблема в этом, но в чем проблема моего кода или блока в KVC_access4 , который НЕ безопасен для одновременного вызова?

Правка и заключение

Благодаря отличному решению BJ Homer , у меня работает NSEnumerationConcurrent. Я тщательно рассчитал оба метода. Приведенный выше код в KVC_access3 быстрее и проще для словарей малого и среднего размера. Это намного быстрее на большом количестве словарей. Однако, если у вас есть большой словарь mongo (миллионы или десятки миллионов пар ключ / значение), тогда этот код:

[subDict 
    enumerateKeysAndObjectsWithOptions:
        NSEnumerationConcurrent
    usingBlock:
        ^(id key, id object, BOOL *stop) {
        NSUInteger workingLength = [object length];
        workingLength += [key length];

        OSAtomicAdd64Barrier(workingLength, &ll); 
 }];

будет до 4 раз быстрее. Точка пересечения для размера - это примерно 1 словарь из 100 000 моих тестовых элементов. Больше словарей и точка кроссовера выше, вероятно, из-за времени настройки.

5
задан Community 23 May 2017 в 10:28
поделиться