Pregunta

De vez en cuando, noto que estoy usando un bloque para iterar sobre una colección sin escribir en ningún dato compartido o causar ningún efecto secundario. Considero agregar una opción de concurrencia NSENUMATER y luego decidir contra ella, ya que realmente no entiendo cuándo vale la pena usarlo.

Así que tengo una pregunta específica y una más general.

Primera pregunta: Aquí hay un ejemplo quizás ligeramente artificial de usar un bloque para hacer algo trivial simultáneamente:

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

Ignorando el hecho de que una enumeración no concesionada podría haber resumido la altura directamente, sin la necesidad de llamar a Malloc o la segunda mitad de la función, ¿hay algún punto usando Nsenumeration Concurrent aquí? ¿La sobrecarga del uso de GCD (o lo que sea Nsenumeration Concurrent en segundo plano) niega la ganancia de obtener una propiedad trivial simultáneamente? ¿Cuánto menos trivial debe obtener el trabajo del bloque antes de que valga la pena usar NsenumerationCurrent?

Segunda pregunta: En términos más generales, debo considerar que la concurrencia es algo que debería usar cuando veo la oportunidad de hacerlo (justificación: presumiblemente el punto de estas API es que hacen que la concurrencia sea menos de un caso especial y más parte del maquillaje general de un programa), o simplemente una optimización que solo debería usar si he encontrado un problema de rendimiento específico y creo que la concurrencia es la respuesta (Justificación: los errores en código concurrente son una pesadilla para rastrear)?

¿Fue útil?

Solución

En general, solo usaría concurrencia cuando las operaciones a realizar son relativamente "pesadas". Incluso entonces, usando la concurrencia en bruto ofrecida por enumerateObjectsWithOptions: Podría ser fácilmente problemático si el paralelismo es la granularidad incorrecta para la tarea en cuestión.

GCD es realmente condenado eficiente para encarear y procesar cosas, pero es muy probable que ese código termine llamando a malloc () para copiar el bloque (depende de si el bloque tiene un estado capturado único).

La respuesta a su segunda pregunta llena muchos libros, más inútiles.

Tomar el código no concesionario y hacerlo concurrente generalmente es un problema muy difícil plagado de insectos de pesadilla. Sin embargo, diseñar para concurrencia por adelantado puede ser excepcionalmente lento. Peor aún, la implementación para una futura concurrencia sin usarla solo lleva a una experiencia de depuración de pesadilla cuando lo enciende.

Un punto clave; Al considerar la concurrencia, concéntrese en hacer sub-gráficos enteros de objetos aislados aislados, salvo una API límite extremadamente bien definida que abarca hilos/colas. Un buen ejemplo de esto son los datos centrales.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top