Reactivecocoa: Atomic: SPINLOCK n'est pas sûr sous iOS

Créé le 15 déc. 2015  ·  20Commentaires  ·  Source: ReactiveCocoa/ReactiveCocoa

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

Je m'abstiendrai de faire des commentaires (je dirai simplement ... si c'est illégal sur iOS, pourquoi est-ce une API publique ???)

Apparemment, les mutex pthread sont plus rapides maintenant . Nous devrions créer un benchmark pour comparer, et essayer de convertir Atomic en ceci (également RACCompoundDisposable et RACSerialDisposable ?)

help wanted

Commentaire le plus utile

J'ai découvert ce fil aujourd'hui, j'ai enquêté sur différents types de synchronisation et exécuté un benchmark sur le simulateur iOS et les appareils réels.
Les résultats sont très intéressants pour moi. Dans iOS 10, nous avons une dégradation visible des performances de dispatch_semaphore, ce qui a probablement changé son comportement avec le respect de la priorité des threads.
Voici le diagramme récapitulatif des mécanismes de synchronisation de base disponibles dans iOS. Tous les tests sont exécutés avec la configuration de la version (optimisation Swift -O)

2016-06-29 17 58 39

Code de référence pour SDK9
Code de référence pour SDK10

Tous les 20 commentaires

Je ne sais même pas ce que signifie «illégal».

nope

Je suis juste curieux, car je n'étais pas là depuis le début, mais pourquoi OSSpinLock utilisé pour le type Atomic pour commencer?

Est-ce que NSLock utilisé puis s'est avéré trop lent, puis pthread_mutex_t été testé et jugé trop lent également? Malheureusement, git n'a pas d'historique avant "déplacer les sources ..."

Les Spinlocks ne sont pas quelque chose que je pense utiliser dans un contexte non-noyau, sans pilote de périphérique, c'est pourquoi je demande. Si quoi que ce soit, je dirais que le bogue ici est que OSSpinLock est utilisé du tout. : stuck_out_tongue:

Pour ajouter un contre-point à mon commentaire ci-dessus, ce n'est pas aussi grave que le titre pourrait le faire croire :

Même en cas de manque de CPU, cette section critique est si petite qu'elle est probablement correcte dans des situations réelles.
- @Catfish_Man sur Twitter

Quant au commentaire ci-dessus de @kastiglione :

Je ne sais même pas ce que signifie «illégal».

Je n'ai pas la réponse, mais je suppose que quelque chose dans le processeur ARM ne permet pas à la primitive spinlock de profiter des mêmes avantages / garanties / hypothèses que sur les processeurs Intel. (Mais probablement pas tous, selon https://twitter.com/Catfish_Man/status/676851988596809728)

Quoi qu'il en soit, il n'y a probablement "rien à voir ici" dans la pratique, et à la place, une enquête devrait être menée pour remplacer par la primitive la plus sûre et mesurer les régressions de performances, comme le suggère @NachoSoto .

Je pense que nous devrions remplacer nos utilisations de OSSpinLock , mais je pense que notre / mon raisonnement pour les utiliser était valide compte tenu de la documentation dont nous disposons.

Ouais, et merci pour l'histoire pertinente commise. Je ne sais pas pourquoi GitHub ne fait pas de git log --follow lorsque l'historique est demandé pour un fichier donné.

En tout cas, je répète que "c'est probablement bien". Utiliser un mutex «maintenant plus rapide» est probablement une tâche simple, mais il l'est moins sans savoir comment vous les avez profilés / testés pour commencer. Vous avez des suggestions / pointeurs là-bas?

Quant à la voie à suivre si / quand les performances deviennent un problème, les files d'attente sans verrouillage méritent d'être étudiées. (Voici un très bon article pour les lecteurs qui ne connaissent pas le concept: http://www.linuxjournal.com/content/lock-free-multi-producer-multi-consumer-queue-ring-buffer?page=0,0). J'ai construit quelque chose de similaire pour une bibliothèque audio interne il y a quelque temps, et je pourrai peut-être l'adapter à un usage général comme celui-ci.

Utiliser un mutex «maintenant plus rapide» est probablement une tâche simple, mais il l'est moins sans savoir comment vous les avez profilés / testés pour commencer. Vous avez des suggestions / pointeurs là-bas?

J'utilisais GitHub Desktop dans le profileur de temps et les allocations d'Instruments, et je me concentrais sur les piles lourdes de RAC. Vous devrez parler à @joshaber ou @mdiep pour cela maintenant. :clin d'œil:

OK bon à savoir. J'imagine que les trucs du côté Swift nécessiteront une base de code tout aussi grande / profonde pour être testée. J'en ai cultivé moi-même ces derniers mois 1 mais je ne sais pas si je pousse RAC "assez fort" pour en tirer suffisamment d'échantillons. Beaucoup d'autres choses ont tendance à apparaître dans les graphiques d'instruments avec des applications comme la mienne. :le sourire:

1 prise ! http://capoapp.com , avec http://capoapp.com/neptune étant le composant le plus récent, totalement conçu à l'aide de RAC4.

J'ai écrit un article de blog aujourd'hui qui explique plus en détail pourquoi les verrous rotatifs sont «illégaux» sur iOS: http://engineering.postmates.com/Spinlocks-Consemed-Harmful-On-iOS/

J'ai interrompu Atomic il y a quelque temps et l'ai migré vers pthread_mutex_lock. De toute évidence, c'est le meilleur verrou qui soit - pas de répartition dynamique comme NSLock, le verrouillage instantané s'il n'est pas soutenu comme le verrou tournant, ne gaspille pas d'énergie contrairement au verrou tournant.

N'hésitez pas à copier-coller la source de mon dépôt ou à en faire ce que vous voulez.

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

@kballard c'est une excellente explication, merci!

Je n'ai pas d'opinions tranchées à ce sujet car je ne suis pas un expert en la matière. Je m'en remettrai à d'autres sur la décision de changer (ou de ne pas changer) le type de serrure que nous utilisons, même si je serais heureux de faire le changement moi-même!

Un benchmark comme celui-là est totalement inutile sans source. De plus, vous n'affichez qu'une seule heure pour chaque construction, sans documenter ce que cette heure représente réellement. Les différentes constructions se comporteront différemment selon qu'elles sont en conflit, les priorités des threads qui se disputent le verrou, etc. @synchronized est aussi une bête curieuse, ses performances dépendront fortement des modèles d'accès , par exemple combien @synchronized blocs

NB: dispatch_semaphore ne donne pas de priorité , ce qui est un autre écueil potentiel.

@jspahrsummers @kballard J'ai une implémentation utilisant atomic CAS. S'il y a un intérêt, je suis heureux de soumettre un PR.

edit: En regardant l'utilisation de Atomic je ne pense pas que mon implémentation sera utile. Si vous pouvez faire des mises à jour idempotentes, CAS vous permet de faire une sorte de transactions optimistes en mémoire, ce qui peut être génial mais qui nécessiterait un peu de refactoring (bien que cela fonctionne très bien si tout est immuable par un CoW, alors vous pouvez vraiment traiter la mutation comme une transaction en mémoire).

J'ai découvert ce fil aujourd'hui, j'ai enquêté sur différents types de synchronisation et exécuté un benchmark sur le simulateur iOS et les appareils réels.
Les résultats sont très intéressants pour moi. Dans iOS 10, nous avons une dégradation visible des performances de dispatch_semaphore, ce qui a probablement changé son comportement avec le respect de la priorité des threads.
Voici le diagramme récapitulatif des mécanismes de synchronisation de base disponibles dans iOS. Tous les tests sont exécutés avec la configuration de la version (optimisation Swift -O)

2016-06-29 17 58 39

Code de référence pour SDK9
Code de référence pour SDK10

Vos chiffres me semblent très suspects. Combien de fois avez-vous exécuté les tests?

À titre de comparaison, j'ai exécuté mon propre benchmark ( source ) sur OS X 10.11.5 en utilisant un harnais de benchmark hacky que j'ai écrit il y a quelque temps. J'ai exécuté le benchmark entier 10 fois, laissé tomber les 2 chiffres du bas et du haut de chaque test (pour me débarrasser des valeurs aberrantes), et le reste des chiffres a produit les plages suivantes:

pas de synchronisation: 22-41ns
Verrouillage tournant: 24ns
sémaphore: 29ns
NSLock: 45-71ns
mutex: 40-64ns
synchronisé: 75-122ns
file d'attente: 505-554ns

De cela, vous pouvez voir que le verrou tournant est de loin le moins cher, suivi du sémaphore, puis du mutex, puis du NSLock juste légèrement derrière le mutex (puisqu'il s'agit essentiellement de mutex + objc_msgSend), puis synchronisé est presque deux fois plus cher, et enfin les files d'attente finissent par être extrêmement cher, beaucoup plus que ce à quoi je m'attendais.

@kballard comme vous pouvez le voir, mon benchmark est assez simple, mais il utilise le mécanisme xctest par défaut pour mesurer le temps d'exécution. Les nombres correspondent à la durée moyenne de chaque test, et non à chaque synchronisation. En général, vos résultats et mes résultats sont corrélés à l'exception des files d'attente, qui ne sont pas si mauvaises, comme nous le pensions

les raisons

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

RuiAAPeres picture RuiAAPeres  ·  3Commentaires

dougmcbride picture dougmcbride  ·  3Commentaires

tunidev picture tunidev  ·  3Commentaires

toddbluhm picture toddbluhm  ·  5Commentaires

simonxcheng picture simonxcheng  ·  6Commentaires