web-dev-qa-db-fra.com

Multitraitement Python avec un cluster distribué

Je recherche un package python capable de traiter de multiples traitements non seulement sur différents cœurs d'un même ordinateur, mais également avec un cluster distribué sur plusieurs ordinateurs. Il existe de nombreux packages python différents pour l'informatique distribuée, mais la plupart semblent nécessiter un changement de code (par exemple, un préfixe indiquant que l'objet se trouve sur une machine distante). Plus précisément, je voudrais quelque chose d'aussi proche que possible de la fonction de multitraitement pool.map. Ainsi, par exemple, si le script est sur une seule machine:

from multiprocessing import Pool
pool = Pool(processes = 8)
resultlist = pool.map(function, arglist)

Ensuite, le pseudocode pour un cluster distribué serait:

from distprocess import Connect, Pool, Cluster

pool1 = Pool(processes = 8)
c = Connect(ipaddress)
pool2 = c.Pool(processes = 4)
cluster = Cluster([pool1, pool2])
resultlist = cluster.map(function, arglist)
23
Michael

Si vous voulez une solution très simple, il n'y en a pas.

Cependant, il existe une solution qui possède l'interface multiprocessing - pathos - qui permet d'établir des connexions avec des serveurs distants via une mappe parallèle et d'effectuer un multitraitement.

Si vous voulez avoir une connexion ssh-tunnelée, vous pouvez le faire… ou si vous êtes d'accord avec une méthode moins sécurisée, vous pouvez aussi le faire.

>>> # establish a ssh tunnel
>>> from pathos.core import connect
>>> tunnel = connect('remote.computer.com', port=1234)
>>> tunnel       
Tunnel('-q -N -L55774:remote.computer.com:1234 remote.computer.com')
>>> tunnel._lport
55774
>>> tunnel._rport
1234
>>> 
>>> # define some function to run in parallel
>>> def sleepy_squared(x):
...   from time import sleep
...   sleep(1.0)
...   return x**2
... 
>>> # build a pool of servers and execute the parallel map
>>> from pathos.pp import ParallelPythonPool as Pool
>>> p = Pool(8, servers=('localhost:55774',))
>>> p.servers
('localhost:55774',)
>>> y = p.map(sleepy_squared, x)
>>> y
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Ou, à la place, vous pouvez configurer pour une connexion directe (pas de ssh)

>>> p = Pool(8, servers=('remote.computer.com:5678',))
# use an asynchronous parallel map
>>> res = p.amap(sleepy_squared, x)
>>> res.get()
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Tout est un peu difficile, pour que le serveur distant fonctionne, vous devez au préalable démarrer un serveur s'exécutant sur remote.computer.com sur le port spécifié - et vous devez vous assurer que les paramètres de votre hôte local et de l'hôte distant vont permettre soit la connexion directe, soit la connexion ssh-tunnelée. De plus, vous devez disposer de la même version de pathos et de la fourchette pathos de pp exécutée sur chaque hôte. De plus, pour ssh, ssh-agent doit être en cours d'exécution pour permettre la connexion sans mot de passe avec ssh.

Mais alors, espérons que tout fonctionne ... si votre code de fonction peut être transporté vers l'hôte distant avec dill.source.importable.

Pour info, pathos est une version attendue depuis longtemps et, en gros, quelques bogues et modifications d’interface doivent être résolus avant la création d’une nouvelle version stable.

9
Mike McKerns

Un peu tard pour le parti ici, mais comme je cherchais aussi une solution similaire, et que cette question n’est toujours pas marquée comme une réponse, j’ai pensé apporter mes conclusions. 

J'ai fini par utiliser SCOOP . Il fournit une implémentation de carte parallèle pouvant fonctionner sur plusieurs cœurs, sur plusieurs hôtes. Il peut également revenir à la fonction serial map de Python si vous le souhaitez au cours de l'appel.

De la page d'introduction de SCOOP, il cite les fonctionnalités suivantes:

Caractéristiques et avantages de SCOOP par rapport aux contrats à terme, au multitraitement et au modules similaires sont les suivants:

  • Exploitez la puissance de plusieurs ordinateurs sur le réseau;
  • Capacité à générer plusieurs tâches dans une tâche;
  • API compatible avec PEP-3148 ;
  • Mise en parallèle du code série avec seulement des modifications mineures;
  • Equilibrage de charge efficace.

Il a quelques défauts (les fonctions/classes doivent être sélectionnables), et la configuration pour que tout fonctionne correctement sur plusieurs hôtes peut être fastidieuse si tous ne partagent pas le même schéma de système de fichiers, mais dans l'ensemble, je suis assez satisfait des résultats . Pour nos besoins, faire un peu de Numpy & Cython, il fournit d'excellentes performances.

J'espère que cela t'aides. 

8
bazel

Je suggérerais de regarder Ray , qui vise à faire exactement cela.

Ray utilise la même syntaxe pour paralléliser le code dans le paramètre multicœur sur un seul ordinateur que dans le paramètre distribué. Si vous êtes prêt à utiliser une boucle for au lieu d'un appel de carte, votre exemple ressemblera à ce qui suit.

import ray
import time

ray.init()

@ray.remote
def function(x):
    time.sleep(0.1)
    return x

arglist = [1, 2, 3, 4]

result_ids = [function.remote(x) for x in arglist]
resultlist = ray.get(result_ids)

Cela exécutera quatre tâches en parallèle en utilisant le nombre de cœurs que vous avez localement. Pour exécuter le même exemple sur un cluster, la seule ligne qui serait modifiée serait l'appel à ray.init(). La documentation pertinente peut être trouvée ici .

Notez que j'aide à développer Ray .

8
Robert Nishihara

Avez-vous cherché à disco ?

Caractéristiques:

  • Carte/Réduire le paradigme
  • Programmation Python
  • Disque partagé distribué
  • ssh sous-couche transport
  • interfaces web et console
  • facile d'ajouter/bloquer/supprimer un nœud
  • master lance les nœuds slaves sans intervention de l'utilisateur
  • Les nœuds slaves sont automatiquement redémarrés en cas d'échec
  • Belle documentation. Suite au Guide d'installation J'ai été en mesure de lancer un cluster à deux machines en quelques minutes (la seule chose à faire était de créer le dossier $ DISCO_HOME/root afin de me connecter à WebUI, je suppose, à cause du journal création d'erreur de fichier).

Un exemple simple tiré de la documentation de disco:

from disco.core import Job, result_iterator

def map(line, params):
    for Word in line.split():
        yield Word, 1

def reduce(iter, params):
    from disco.util import kvgroup
    for Word, counts in kvgroup(sorted(iter)):
        yield Word, sum(counts)

if __== '__main__':
    job = Job().run(input=["http://discoproject.org/media/text/chekhov.txt"],
                    map=map,
                    reduce=reduce)
    for Word, count in result_iterator(job.wait(show=True)):
        print(Word, count)
0
asterio gonzalez