1. John Siracusa
    1. Mountain Lion
      1. Introduction
      2. Achat et installation
      3. Changements d'interface (1)
      4. Changements d'interface (2)
      5. Changements d'interface (3)
      6. Applications (1)
      7. Applications (2)
      8. Applications (3)
      9. Applications (4)
      10. Applications (5)
      11. iCloud(1)
      12. iCloud(2)
      13. iCloud(3)
      14. Gatekeeper(1)
      15. Gatekeeper(2)
      16. Retina et HiDPI
      17. Fourre-tout (1)
      18. Fourre-tout (2)
      19. Fourre-tout (3)
      20. Fourre-tout (4)
      21. Fourre-tout (5)
      22. Fourre-tout (6)
      23. Recommandations
      24. Deux pères, un fils
    2. Lion
      1. Introduction
      2. Installation
      3. Revoir les fondamentaux
      4. Redimensionnement des fenêtres
      5. Et voici pour les cinglés
      6. La gestion des fenêtres
      7. Le modèle de document
      8. le modèle des processus
      9. Les éléments internes (1)
      10. Les éléments internes (2)
      11. ARC
      12. Le système de fichiers
      13. Ses modifications dans Lion
      14. Documents, résolution
      15. Le Finder
      16. Mail, Safari
      17. Fourre tout (1)
      18. Fourre tout (2)
      19. Recommendations
    3. Snow Leopard
      1. Introduction
      2. Le ticket d'entrée
      3. L'installation
      4. Nouvel aspect
      5. Détails internes
      6. Quick Time X
      7. Système de fichiers
      8. Faire plus avec plus
      9. LLVM et Clang
      10. Les blocs
      11. Concurrence
      12. Grand Central Dispatch
      13. Asynchronicité
      14. Open CL
      15. La différence...
      16. Quick Time Player
      17. Le Dock
      18. Le Finder
      19. Exchange
      20. Performances
      21. Fourre tout (1)
      22. Fourre tout (2)
      23. Le futur
    4. Leopard
      1. Introduction
      2. L'héritage
      3. Nouvel aspect 1
      4. Nouvel aspect 2
      5. Le noyau
      6. 64 bits
      7. FS Events
      8. Core animation
      9. Quartz GL
      10. Core UI
      11. Détails internes
      12. Le Finder
      13. Le Dock
      14. Time Machine
      15. Performances
      16. Pot pourri
      17. Demain
    5. Tiger
      1. Introduction
      2. Retour sur le passé
      3. Nouvel aspect de Tiger
      4. Mises à jour du noyau
      5. Le lancement
      6. Les méta-données
      7. Attributs étendus
      8. Listes de contrôle d'accès
      9. Spotlight 1
      10. Spotlight 2 : analyse et potentiel
      11. Types de fichiers
      12. Méta-données : la fin
      13. Quartz
      14. Quartz 2D Extreme
      15. Core Image
      16. La vidéo sous Tiger
      17. Dashboard
      18. Le Finder
      19. Les performances
      20. Pot pourri
      21. Conclusion
    6. Panther
      1. Introduction
      2. Les précédents
      3. L'installation
      4. Nouvel aspect
      5. Performances
      6. Changement rapide d'utilisateur
      7. Gestion des fenêtres
      8. Exposé
      9. Le Finder
      10. Performance du Finder
      11. Toujours le même
      12. Safari
      13. XCode
      14. Conclusion
    7. Jaguar
      1. Introduction
      2. Le nom
      3. L'installation
      4. Modifications d'Unix
      5. Dévelopeurs...
      6. Quoi de neuf
      7. Rendezvous
      8. Quartz Extrême
      9. Performance
      10. Compositions
      11. Le Finder
      12. Applications
      13. Sherlock
      14. Le portrait
    8. Puma
      1. Prelude
      2. Introduction
      3. Installation
      4. Réglages système
      5. Performance
      6. Redimensionnement des fenêtres
      7. Utilisation de la mémoire
      8. Diagnostics de mémoire
      9. L'environnement Classique
      10. L'interface Utilisateur
      11. Le Finder
      12. Extensions de fichiers
      13. Divers, conclusion
    9. Cheeta (Mac OS X 10.0)
      1. Qu'est ce que Mac OS X
      2. Installation
      3. Le démarrage
      4. Utilisation de la RAM
      5. Performance
      6. Performance des applications
      7. Stabilité
      8. L'interface utilisateur
      9. Le Finder
      10. Le navigateur du Finder
      11. Le Finder (divers)
      12. L'interface utilisateur
      13. Os X Classique
      14. Système de fichiers
      15. Unix
      16. Applications
      17. Conclusion
    10. Les débuts de MacOsX
      1. 1999 : OSX DP2
      2. 2000 : Quartz et Aqua/a>
      3. Fin de la lune de miel
      4. la première bêta publique
      5. 2001 : Mac OS X 10.0
      6. Un investissement
    11. Finder Spatial
      1. Introduction
      2. Interfaces spatiales
      3. Le Finder spatial
      4. Le concierge
      5. Un nouveau Finder
      6. Le browser
      7. Le browser spatial
      8. Finder et méta-données
      9. Les modèles
      10. Pensées finales

Grand Central Dispatch




GCD

La marque Apple de GDC (le service du rail)

La réponse de Léopard des neiges aux cafouillages de la simultanéité s'appelle Grand Central Dispatch (GCD). Comme pour QuickTime X, le nom est très bien choisi, quoi qu'il ne soit pas entièrement compréhensible avant que vous ayez compris la technologie.


La première chose à savoir au sujet de GCD est que ce n'est pas un nouveau framework Cocoa ou un ornement spécifique de ce genre. C'est une bibliothèque C complète, enchâssée dans les couches les plus élémentaires de Mac OS X. (Il est dans libSystem, qui contient aussi libc, et d'autre code, qui se place tout à fait à la base de l'espace utilisateur).


Il n'est pas nécessaire de lier une nouvelle bibliothèque quand vous utilisez GCD dans votre programme. insérez seulement <dispatch/dispatch.h> et vous voilà dans la course. Le fait que GCD est une bibliothèque C signifie qu'on peut l'utiliser avec tous les langages dérivés du C supportés par Mac OS X : objective C, C++ et Objective C++.

Les queues et les threads

GCD est basé sur quelques entités simples. Commençons par les queues. Une queue dans GCD est juste ce que cela suggère. Les tâches sont insérées dans la queue, et retirées de la queue à l'aide d'une procédure FIFO. (Cela veut dire "premier entré, premier sorti", comme la queue à la caisse d'un supermarché, pour ceux qui ne voudraient pas suivre le lien précédent). Retirer une tâche de la queue consiste à la passer à un thread qui va provoquer son exécution et faire le travail qu'elle doit faire.

Bien que les queues de GCD transfèrent les tâches à des threads dans un ordre FIFO, plusieurs tâches d'une même queue peuvent être exécutées en parallèle à un moment donné comme le montre cette animation.

Cœurs inutilisés

Lancez l'animation pour voir la queue de GCD en action

Remarquez que la tâche B se termine avant la tâche A. Bien que la sortie de queue soit en FIFO, l'exécution des tâches ne l'est pas. Et notez que, bien qu'il y ait eu trois tâches dans la queue, deux threads seulement ont été utilisés. C'est une caractéristique importante de GCD que nous allons voir bientôt.

Mais d'abord, considérons une autre forme de queue. Une queue sérielle fonctionne comme une queue normale, mais n'exécute qu'une tâche à la fois. Cela veut dire que, dans une queue sérielle, l'exécution des tâches est aussi FIFO. Des queues sérielles peuvent être créées explicitement, comme des queues normales, mais chaque application a aussi sa propre "queue principale", implicite, qui est une queue sérielle, et qui s'exécute sur le thread principal.

L'animation montre que des threads sont créés quand il y a du travail à faire, et disparaissent quand on n'en a plus besoin. D'où viennent ces threads, et où vont-ils quand ils ont fini ? GDC maintient un paquet global de threads qu'il attribue aux queues quand elles en ont besoin. Quand une queue n'a plus de tâches en attente à fournir au thread, le thread retourne dans le paquet.

Ceci est un aspect très important de la conception de GCD. D'une façon étonnante, l'une des choses les plus difficiles pour obtenir le maximum de performances à partir d'un ensemble de threads gérés manuellement est de se représenter exactement combien il faut créer de threads. Trop peu, et vous laissez votre matériel inoccupé. Trop de threads, et vous passez une partie importante du temps à les répartir entre les cœurs disponibles.

Supposons qu'un programme a un problème qui peut être scindé en 8 unités de travail séparées, indépendantes. Si ce programme crée quatre threads sur une machine à 8 cœurs, est-ce que c'est trop, ou trop peu de threads ? Question piège ! La réponse est que cela dépend de ce qui se passe en plus sur le système.

Si six des huit cœurs sont complètement saturés quand il font un autre travail, la création de quatre threads obligera l'OS à gaspiller du temps à faire alterner ces quatre threads sur les deux cœurs disponibles. Mais si le processus qui saturait les six cœurs se termine ? Il y a alors huit cœurs disponibles, mais seulement quatre threads, ce qui laisse la moitié des cœurs inoccupés.

A l'exception des programmes qui peuvent raisonnablement s'attendre à utiliser la totalité de la machine pour leur exécution, il n'y a aucun moyen pour le programmeur de prévoir à l'avance combien de threads il doit créer. Parmi les cœurs d'une machine, combien sont utilisés ? Si des cœurs deviennent disponibles, comment mon programme peut-il le savoir ?

La conclusion, c'est que ce nombre optimum de threads à mettre en jeu à un moment donné peut être déterminé par une entité unique, qui est à même de connaître ce qui se passe. Sous Léopard des neiges, cette entité s'appelle GCD. Elle va maintenir zéro threads dans son paquet s'il n'y a aucune queue avec des tâches à exécuter. A mesure que les tâches sont retirées de la queue, GCD va créer et distribuer les threads de façon à optimiser le matériel disponible. GCD connaît le nombre de cœurs du système, et il sait combien de threads sont en train d'exécuter des tâches à un moment donné. Quand une queue n'a plus besoin d'un thread, il est remis dans le paquet, où GCD peut le réutiliser pour une autre queue qui a une tâche en attente.

Il y a d'autres optimisations inhérentes à ce schéma. Dans Mac OS X, les threads sont relativement chargés. Chaque thread maintient son propre ensemble de valeurs de registre (register), son pointeur de pile, et son compteur ordinal, plus des structures de données du noyau qui vérifient ses certifications de sécurité, la priorité d'exécution, un ensemble de signaux en cours d'utilisation et de masques de signaux, etc... Tout cela rajoute jusqu'à 512 Ko de mémoire supplémentaire (overhead, ou à côtés) par thread. Créez un millier de threads, et vous avez avez brûlé environ un demi Go de mémoire et de ressources du noyau seulement en à côtés, en plus des données effectives de chaque thread.

Comparez un thread avec un bagage de 512 Ko avec les queues de GCD qui ont simplement 256 octets d'overhead. Les queues sont très légères, et les développeurs sont encouragés à en créer autant qu'ils en ont besoin, même des milliers. Dans l'animation, quand la queue reçoit deux threads pour exécuter ses trois tâches, elle exécute deux tâches dans l'un des threads. Non seulement les threads sont lourds en terme de mémoire utilisée en overhead, mais ils sont aussi coûteux à créer. Créer un nouveau thread pour chaque tâche serait le scénario le plus mauvais possible. A chaque fois que GCD utilise un thread pour exécuter plus d'une tâche, on y gagne en efficacité totale du système.

Rappelez-vous le problème du programmeur cherchant à se représenter combien il doit créer de threads. S'il utilise GCD, il n'a pas besoin de s'embêter avec tout ça. Il peut à la place se concentrer à optimiser dans l'abstrait la simultanéité de son algorithme. Si le scénario du meilleur cas pour ce problème devrait utiliser 500 tâches concurrentes, il peut y aller et créer 500 queues GCD, et distribuer son travail entre elles. GCD va s'arranger pour définir combien de threads il doit créer pour faire le travail. Et en plus, il va ajuster le nombre de threads de façon dynamique à mesure que les conditions changent dans le système.

Mais ce qui est encore plus important, à mesure que de nouveaux matériels sont disponibles avec de plus en plus de cœurs de CPUs, le programmeur n'a pas besoin de changer son application. GCD, va s'adapter de façon transparente à toutes les ressources de calcul disponibles, jusqu'à un optimum de simultanéité (mais pas au delà) tel qu'il a été défini par le programmeur quand il a choisi le nombre de queues à créer.

Mais attendez, il y a plus encore ! les queues GCD peuvent être organisées en graphes orientés acycliques de complexité arbitraire. (En fait, ils peuvent être cycliques aussi, mais leur comportement n'est pas défini ; ne faites pas ça). Des hiérarchies de queues peuvent être utilisées pour canaliser les tâches à partir de sous-systèmes disparates, en un ensemble plus étroit de queues contrôlées de façon coordonnée, et pour forcer un ensemble de queues normales à déléguer leur travail à une queue sérielle, ce qui indirectement, les sérialise toutes.

Il y a aussi plusieurs niveaux de priorité pour les queues, qui définissent à quel rythme et avec quelle priorité les threads leur sont attribués à partir du paquet. Les queues peuvent être suspendues, reprises, et annulées. Les queues peuvent aussi être regroupées ce qui permet à toutes les tâches fournies au groupe d'être suivies et de compter pour une unité.

Finalement, l'utilisation des queues et des threads dans GCD représente une architecture simple, élégante, mais aussi très pragmatique.