L'informatique parallèle ou distribuée est en soi une chose très simple. Et l'environnement de développement devrait prendre en charge, et le spécialiste DS devrait avoir les compétences nécessaires pour effectuer le calcul parallèle, et la tâche devrait être réduite à une forme qui peut être divisée en parties, le cas échéant. Mais avec une approche compétente, vous pouvez accélérer considérablement la solution du problème avec le R à un seul thread, si vous avez au moins un processeur multicœur (et presque tout le monde l'a maintenant), ajusté pour la limite d'accélération théorique déterminée par la loi d'Amdal . Cependant, dans certains cas, même elle peut être contournée.
Il s'agit d'une continuation des publications précédentes .
Approche typique
En règle générale, lorsqu'un analyste (spécialiste DS, développeur ou choisissez un nom approprié pour vous-même) essaie d'accélérer la tâche au sein d'un ordinateur et commence à passer du mode monothread au mode multithread, il le fait de manière standard. parApply
, foreach\%dopar%
, etc. Vous pouvez voir de manière compacte et intelligible, par exemple, ici: «Parallélisme dans R» . 3 étapes:
- faire du fil
core-1
- courir en utilisant
foreach
, - recueillir les réponses et obtenir le résultat.
Pour les tâches informatiques typiques qui occupent 100% du CPU et ne nécessitent pas le transfert d'une grande quantité d'informations d'entrée, c'est la bonne approche. Le point principal qui nécessite une attention est de fournir une journalisation dans les threads afin de pouvoir contrôler le processus. Sans enregistrement, le vol se déroulera même sans instruments.
Dans le cas des tâches "d'entreprise", lorsqu'elles sont parallélisées, de nombreuses difficultés méthodologiques supplémentaires se posent qui réduisent considérablement l'effet de l'approche simple ci-dessus:
- fort déséquilibre possible de la charge sur les flux;
- Les exigences de performances du processeur dans une seule tâche peuvent être déchirées avec seulement quelques rafales pointues;
- chaque calcul individuel peut nécessiter une quantité importante de mémoire pour l'entrée et la sortie de résultats également de taille considérable;
- dans le cadre d'une tâche distincte, il peut y avoir un mélange entre l'informatique, l'utilisation du disque et l'interrogation des systèmes externes.
Il s'agit d'un scénario tout à fait typique lorsque, dans le cadre du processus, vous devez obtenir un travail volumineux en entrée, lire les données du disque, récupérer une grande partie de la base de données, demander des systèmes externes et attendre une réponse de leur part (classique - demande d'API REST), puis renvoyer N au processus parent mégaoctets en conséquence.
Map-reduce
par les utilisateurs, les emplacements, les documents, les adresses IP, les dates, ... (ajoutez-le vous-même). Dans les cas les plus tristes, l'exécution parallèle peut être plus longue que celle à un seul thread. Des problèmes de mémoire insuffisante peuvent également survenir. Tout est-il parti? Pas du tout.
Voie alternative
Examinons la thèse d'une manière d'améliorer radicalement la situation. Dans le même temps, nous n'oublions pas que nous vivons dans le cadre d'un zoo plein. Circuit productif sur *nix
, ordinateurs portables DS sur Win * nix \ MacOS, mais il faut qu'il fonctionne uniformément partout.
- Une microtâche: a reçu une entrée utilisateur, a demandé une base de données, a demandé 2 circuits intégrés externes via REST, a téléchargé et analysé un répertoire à partir d'un disque, a effectué un calcul, a vidé le résultat dans disk \ database. Utilisateurs, par exemple,
10^6
. - Nous passons à l'utilisation du
future
package et de l'adaptateur universel doFuture
. - Si des tâches distinctes sont telles que, dans des tâches distinctes, le temps de processeur est nécessaire en petite quantité (nous attendons des réponses de systèmes tiers),
doFuture
vous permet de passer du fractionnement des doFuture
au fractionnement en processus séparés sur une seule ligne (vous pouvez voir les paramètres de démarrage dans *nix
dans htop
) . - Ces processus peuvent être créés bien plus que des cœurs. Aucun clinchage ne se produira car les processus individuels sont en mode veille la plupart du temps. Mais il sera nécessaire de sélectionner expérimentalement le nombre optimal de processus sur la base du cyclogramme d'un processus de traitement typique.
Résultat - la tâche d'origine est beaucoup plus rapide. L'accélération peut être encore plus grande que le nombre de cœurs disponibles.
Il n'y a pas de code consciemment, car la tâche principale de la publication est de partager l'approche et une excellente famille de future
packages.
PS
Il y a quelques petites nuances qui doivent également être tracées:
- chaque processus consommera de la mémoire, y compris les données reçues et renvoyées. Une augmentation du nombre de processus multipliera les besoins en RAM disponible.
doFuture
utilise la «magie» pour déterminer automatiquement la composition des variables et des paquets transférés au processus, mais vous ne devez pas tout laisser aller, il vaut mieux vérifier.- dans les processus, le contrôle
gc
explicite et la suppression explicite de variables à l'aide de rm
ne nuiront pas. Ce n'est pas une panacée et peut ne pas fonctionner , mais l'indication explicite des objets supprimés sera utile. - une fois le calcul terminé, appelez le
plan(sequential)
. Cela fermera tous les processus et libérera la mémoire qu'ils occupent. - Si vous devez transférer une grande quantité de données vers le processus, pensez à utiliser un stockage externe (disque, base de données). N'oubliez pas que les descripteurs ne peuvent pas être transférés, la source doit être ouverte à l'intérieur du processus lui-même.
Publication précédente - «Processus commerciaux dans les entreprises: spéculation et réalité. On fait la lumière avec R " .