Подтвердить что ты не робот

Когда использовать NSEnumerationConcurrent

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

Итак, у меня есть конкретный вопрос и более общий.

Первый вопрос: вот, может быть, немного надуманный пример использования блока для выполнения чего-то тривиального одновременно:

CGFloat GetAverageHeight(NSArray* people)
{
  NSUInteger count = [people count];
  CGFloat* heights = malloc(sizeof(CGFloat) * count);

  [people enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock:
  ^(id person, NSUInteger idx, BOOL* stop)
  {
    heights[idx] = [person height];
  }];

  CGFloat total= 0.0;
  for (size_t i = 0 ; i < count ; i++) total += heights[i];
  free(heights);
  return total / count;
}

Игнорируя тот факт, что неконкурентное перечисление могло просто подвести высоту напрямую, без необходимости вызова malloc или второй половины функции, существует ли какая-либо точка, использующая здесь NSEnumerationConcurrent? Не влияет ли накладные расходы на использование GCD (или что-то, что NSEnumerationConcurrent делает в фоновом режиме) отрицательно влияет на получение тривиального свойства одновременно? Насколько менее тривиальным является необходимость работы блока, прежде чем он сможет использовать NSEnumerationConcurrent?

Второй вопрос: в общем, следует ли считать concurrency тем, что я должен использовать, когда вижу возможность сделать это (логическое обоснование: предположительно, точка этих API-интерфейсов заключается в том, что они уменьшают concurrency от частного случая и большую часть общей составляющей программы), или просто оптимизацию, которую я должен использовать, только если я нашел конкретную проблему с производительностью и считаю, что concurrency является ответом (логическое обоснование: ошибки в параллельном коде - это кошмар для отслеживать)?

4b9b3361

Ответ 1

Как правило, вы используете только concurrency, когда выполняемые операции относительно "тяжелые". Даже тогда использование raw concurrency, предлагаемое enumerateObjectsWithOptions:, может быть проблематичным, если parallelism является неправильной детализацией для этой задачи.

GCD действительно эффективен при загрузке и обработке файлов, но этот код, скорее всего, закончится вызовом malloc() для копирования блока (зависит от того, имеет ли блок уникальное состояние захвата).

Ответ на ваш второй вопрос заполняет многие книги, самые бесполезные.

Принимая неконкурентный код и делая его одновременным, как правило, очень трудная задача изобилует кошмарными ошибками. Тем не менее, проектирование для concurrency спереди может быть исключительно трудоемким. Хуже того, реализация на будущее concurrency без фактического использования его просто приводит к кошмарному отладке, когда вы его включаете.

Один ключевой момент; при рассмотрении concurrency сосредоточьтесь на создании целых субграфов объектов, изолированных от потоков, за исключением чрезвычайно хорошо определенных границ API, которые охватывают потоки/очереди. Хорошим примером этого является Core Data.