Reactivecocoa: Atómico: SPINLOCK no es seguro en iOS

Creado en 15 dic. 2015  ·  20Comentarios  ·  Fuente: ReactiveCocoa/ReactiveCocoa

https://twitter.com/steipete/status/676851647042203648

Me abstendré de hacer comentarios (solo diré ... si es ilegal en iOS, ¿por qué es una API pública?)

Aparentemente, los mutex de pthread ahora son más rápidos . Deberíamos crear un punto de referencia para comparar e intentar convertir Atomic a esto (también RACCompoundDisposable y RACSerialDisposable ?)

help wanted

Comentario más útil

Descubrí este hilo hoy, hice una investigación de diferentes tipos de sincronización y ejecuté un punto de referencia en un simulador de iOS y dispositivos reales.
Los resultados son muy interesantes para mí. En iOS 10 tenemos una degradación visible del rendimiento de dispatch_semaphore, que probablemente ha cambiado su comportamiento con respecto a la prioridad del hilo.
Aquí está el diagrama de resumen de los mecanismos básicos de sincronización disponibles en iOS. Todas las pruebas se ejecutan con la configuración de lanzamiento (optimización Swift -O)

2016-06-29 17 58 39

Código de referencia para SDK9
Código de referencia para SDK10

Todos 20 comentarios

Ni siquiera estoy seguro de lo que significa "ilegal".

nope

Solo tengo curiosidad, ya que no estaba presente desde el principio, pero ¿por qué se usó OSSpinLock para el tipo Atomic para empezar?

¿Se usó NSLock y luego se demostró que era demasiado lento, luego se probó pthread_mutex_t y también se encontró que era demasiado lento? Desafortunadamente, git no tiene historial antes de "mover fuentes ..."

Los spinlocks no son algo que piense usar en un contexto que no sea de kernel ni de controlador de dispositivo, por eso pregunto. En todo caso, diría que el error aquí es que OSSpinLock se usa en absoluto. : Lengua_salida_atascada:

Para agregar un contrapunto a mi comentario anterior, esto no es tan importante como podría parecer el título: https://twitter.com/Catfish_Man/status/676854531615883265

Incluso bajo falta de CPU, esa sección crítica es tan pequeña que probablemente esté bien en situaciones reales.
- @Catfish_Man en Twitter

En cuanto al comentario anterior de @kastiglione :

Ni siquiera estoy seguro de lo que significa "ilegal".

No tengo la respuesta, pero supongo que algo en la CPU ARM no permite que la primitiva spinlock disfrute de muchos de los mismos beneficios / garantías / suposiciones que tiene en las CPU Intel. (Pero probablemente no todos, según https://twitter.com/Catfish_Man/status/676851988596809728)

De todos modos, es probable que "no haya nada que ver aquí" en la práctica y, en su lugar, se debe realizar una investigación para reemplazar con la primitiva más segura y medir cualquier regresión de rendimiento, como sugiere @NachoSoto .

Creo que deberíamos reemplazar nuestros usos de OSSpinLock , pero creo que nuestro / mi razonamiento para usarlos fue válido dada la documentación disponible para nosotros.

Sí, y gracias por los compromisos históricos relevantes. No estoy seguro de por qué GitHub no hace un git log --follow cuando se solicita el historial para un archivo determinado.

De todos modos, reiteraré que "probablemente esté bien". Usar un mutex "ahora más rápido" es probablemente una tarea sencilla, sin embargo, no lo es tanto sin saber cómo los perfilaste / probó para empezar. ¿Tiene alguna sugerencia / consejo allí?

En cuanto a un camino a seguir si / cuando el rendimiento se convierte en un problema, vale la pena investigar las colas sin bloqueos. (Aquí hay un artículo bastante bueno para lectores que no estén familiarizados con el concepto: http://www.linuxjournal.com/content/lock-free-multi-producer-multi-consumer-queue-ring-buffer?page=0,0). Construí algo similar para una biblioteca de audio interna hace un tiempo, y es posible que pueda adaptarlo para un uso general como este.

Usar un mutex "ahora más rápido" es probablemente una tarea sencilla, sin embargo, no lo es tanto sin saber cómo los perfilaste / probó para empezar. ¿Tiene alguna sugerencia / consejo allí?

Estaba ejecutando GitHub Desktop en Asignaciones y perfil de tiempo de Instruments, y me enfocaba en pilas de RAC. Tendrás que hablar con @joshaber o @mdiep para eso ahora. :guiño:

Bien, es bueno saberlo. Me imagino que las cosas del lado de Swift requerirán una base de código igualmente grande / profunda para probar. He cultivado algo por mi cuenta en los últimos meses 1 pero no sé si presiono a RAC "lo suficiente" para obtener suficientes muestras. Muchas otras cosas tienden a aparecer en las listas de instrumentos con aplicaciones como la mía. :sonrisa:

1 enchufe! http://capoapp.com , siendo http://capoapp.com/neptune el componente más reciente, totalmente diseñado con RAC4.

Hoy escribí una publicación de blog que explica con más detalle por qué los spinlocks son "ilegales" en iOS: http://engineering.postmates.com/Spinlocks-Considered-Harmful-On-iOS/

Rompí Atomic hace un tiempo y lo migré a pthread_mutex_lock. Según todas las cuentas, es el mejor bloqueo que existe: no hay despacho dinámico como NSLock, bloqueo instantáneo si no se sostiene como spinlock, no desperdicia energía a diferencia del bloqueo de giro.

Siéntase libre de copiar y pegar la fuente de mi repositorio o hacer lo que quiera con él.

https://github.com/Adlai-Holler/Atomic
https://github.com/Adlai-Holler/Atomic/blob/master/Atomic/Atomic.swift

@kballard esa es una gran explicación, ¡gracias!

No tengo opiniones sólidas al respecto ya que no soy un experto en la materia. Dejaré que otros tomen la decisión de cambiar (o no cambiar) el tipo de candado que usamos, ¡aunque me encantaría hacer el cambio yo mismo!

Un punto de referencia como ese es completamente inútil sin fuente. Además, solo muestra un tiempo para cada construcción, sin documentar lo que ese tiempo realmente representa. Las diversas construcciones se comportarán de forma diferente dependiendo de si están en disputa, las prioridades de los hilos que están contendiendo en la cerradura, etc. @synchronized es también un animal curioso, su rendimiento dependerá en gran medida de los patrones de acceso , como cuántos bloques @synchronized se ingresan / salen al mismo tiempo, ya sea que esté bloqueando el mismo objeto repetidamente o bloqueando nuevos objetos, etc.

NB: dispatch_semaphore no dona prioridad , lo cual es otro peligro potencial.

@jspahrsummers @kballard Tengo una implementación usando atomic CAS. Si hay algún interés, me complace enviar un PR.

editar: Mirando el uso de Atomic no creo que mi implementación sea útil. Si puede hacer actualizaciones idempotentes, CAS le permite hacer una especie de transacciones optimistas en la memoria que pueden ser geniales pero que requerirían un poco de refactorización (aunque funciona muy bien si todo es un CoW inmutable, entonces realmente puede tratar la mutación como una transacción en memoria).

Descubrí este hilo hoy, hice una investigación de diferentes tipos de sincronización y ejecuté un punto de referencia en un simulador de iOS y dispositivos reales.
Los resultados son muy interesantes para mí. En iOS 10 tenemos una degradación visible del rendimiento de dispatch_semaphore, que probablemente ha cambiado su comportamiento con respecto a la prioridad del hilo.
Aquí está el diagrama de resumen de los mecanismos básicos de sincronización disponibles en iOS. Todas las pruebas se ejecutan con la configuración de lanzamiento (optimización Swift -O)

2016-06-29 17 58 39

Código de referencia para SDK9
Código de referencia para SDK10

Sus números me parecen muy sospechosos. ¿Cuántas veces realizaste las pruebas?

A modo de comparación, ejecuté mi propio punto de referencia ( fuente ) en OS X 10.11.5 usando un arnés de referencia hacky que escribí hace un tiempo. Ejecuté el punto de referencia completo 10 veces, eliminé los 2 números inferior y superior de cada prueba (para eliminar los valores atípicos) y el resto de los números produjeron los siguientes rangos:

sin sincronización: 22-41ns
spinlock: 24ns
semáforo: 29ns
NSLock: 45-71ns
mutex: 40-64ns
sincronizado: 75-122ns
cola: 505-554ns

A partir de esto, puede ver que spinlock es, con mucho, el más barato, seguido de semáforo, luego mutex, luego NSLock ligeramente detrás de mutex (ya que es básicamente mutex + objc_msgSend), luego sincronizado es casi dos veces más caro, y finalmente las colas terminan siendo extremadamente caro, mucho más de lo que esperaba.

@kballard, como puede ver, mi punto de referencia es bastante simple, pero utiliza el mecanismo xctest predeterminado para medir el tiempo de ejecución. Los números son el tiempo de ejecución promedio para cada prueba, no para cada sincronización. En general, tus resultados y los míos se correlacionan excepto las colas, que no son tan malas como solíamos pensar.

razones

¿Fue útil esta página
0 / 5 - 0 calificaciones