2011-07-24 15 views
13

De vez en cuando, veo que estoy usando un bloque para iterar sobre una colección sin escribir en ningún dato compartido ni causar ningún efecto secundario. Considero agregar una opción NSEnumerationConcurrent, luego decido no hacerlo ya que realmente no entiendo cuándo vale la pena usarla.Cuándo utilizar NSEnumerationConcurrent

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

Primera pregunta: He aquí un ejemplo tal vez un poco artificiosa de la utilización de un bloque de 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 concurrente podría haber simplemente resumió la altura directamente, sin la necesidad para llamar a malloc o la segunda mitad de la función, ¿hay algún punto usando NSEnumerationConcurrent aquí? ¿Niega la sobrecarga de usar GCD (o lo que sea que haga NSEnumerationConcurrent en el fondo) negar la ganancia de obtener una propiedad trivial al mismo tiempo? ¿Cuánto menos trivial necesita el trabajo del bloque antes de que valga la pena usar NSEnumerationConcurrent?

Segunda pregunta: más en general, ¿debería considerar la concurrencia como algo que debería usar cuando veo la oportunidad de hacerlo (justificación: presumiblemente el objetivo de estas API es que hacen que la concurrencia sea menos un caso especial y más parte de la configuración general de un programa), o simplemente una optimización que solo debería usar si encuentro un problema de rendimiento específico y creo que la concurrencia es la respuesta (razón de ser: los errores en el código simultáneo son una pesadilla para rastrear).

Respuesta

10

Generalmente, solo usaría concurrencia cuando las operaciones que se van a realizar sean relativamente "pesadas". Incluso entonces, usar la concurrencia bruta 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 muy eficiente para enrutar y procesar cosas, pero ese código es muy probable que 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, la mayoría inútiles.

Tomar código no simultáneo y hacerlo concurrente es generalmente un problema muy difícil plagado de errores de pesadilla. Sin embargo, diseñar para la concurrencia desde el principio puede consumir mucho tiempo. Peor aún, implementar la simultaneidad futura sin usarla realmente solo lleva a una experiencia de depuración de pesadilla cuando la enciendes.

Un punto clave; Al considerar la simultaneidad, concéntrese en hacer que los subgráficos completos de objetos estén aislados por hilos, excepto por la API de límites muy bien definida que abarca hilos/colas. Un buen ejemplo de esto es Core Data.

Cuestiones relacionadas