J'ai porté l'exemple de calcul parallèle de la documentation Python vers F #. Je pense que vous pouvez commencer en comparant les styles d'écriture.
GIL
J'ai essayé le multithreading en Python.
J'ai essayé le calcul parallèle, mais cela ne s'est pas beaucoup amélioré. Les articles suivants sont mentionnés.
Python utilise "Global Interpreter Lock (GIL)" comme mécanisme pour garantir la cohérence de l'exécution du programme. blockquote>Apparemment, Python Thread est pris en charge pour gérer le "blocage des E / S" lors des appels système ... Il semble que cela ne vise pas à accélérer en premier lieu ... .. blockquote>La documentation explique comment.
(Global Interpreter Lock) Un mécanisme utilisé par l'interpréteur CPython pour garantir qu'un seul thread exécute le bytecode Python à la fois. Cela simplifie l'implémentation de CPython en rendant le modèle objet (y compris les types intégrés importants tels que dict) implicitement sûr pour l'accès simultané. Le verrouillage de l'intégralité de l'interpréteur facilite la lecture multithread de l'interpréteur au détriment de la parallélisation encourue par les machines multiprocesseurs.
Cependant, certains modules d'extension standards ou externes sont conçus pour désactiver GIL lors de calculs lourds tels que la compression et le hachage. De plus, GIL est toujours libéré lors du traitement des E / S.
Dans le passé, des interpréteurs "multithread gratuits" (qui verrouillent les données en service à un grain plus fin) ont été développés, mais ont échoué en raison de performances médiocres sur un processeur unique typique. On pense que les tentatives pour résoudre ce problème de performances rendent la mise en œuvre plus complexe et augmentent les coûts de maintenance.
ProcessPoolExecutor
Les calculs parallèles en Python sont multi-processus plutôt que multi-thread. ProcessPoolExecutor prend en charge la gestion des processus et l'appel de fonctions entre les processus. Par défaut, il crée autant de processus de travail qu'il y a de processeurs logiques et alloue le traitement.
Modifiez l'exemple ProcessPoolExecutor dans la documentation. Vérifiez le temps requis en basculant entre parallèle et série. Vous pouvez le comparer avec le multithreading simplement en le remplaçant par ThreadPoolExecutor.
- Modifié à partir de concurrent.futures - Exécution de tâche parallèle (exemple ProcessPoolExecutor)
parallel-sample.py
import concurrent.futures import math PRIMES = [ 112272535095293, 112582705942171, 112272535095293, 115280095190773, 115797848077099, 1099726899285419] def is_prime(n): if n < 2: return False if n == 2: return True if n % 2 == 0: return False sqrt_n = int(math.floor(math.sqrt(n))) for i in range(3, sqrt_n + 1, 2): if n % i == 0: return False return True def main_parallel(cls): with cls() as executor: for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): print('%d is prime: %s' % (number, prime)) def main(): for number, prime in zip(PRIMES, map(is_prime, PRIMES)): print('%d is prime: %s' % (number, prime)) if __name__ == '__main__': import sys if sys.argv[-1] == "--process": main_parallel(concurrent.futures.ProcessPoolExecutor) elif sys.argv[-1] == "--thread": main_parallel(concurrent.futures.ThreadPoolExecutor) else: main()
Résultat d'exécution
$ time python parallel-sample.py 112272535095293 is prime: True 112582705942171 is prime: True 112272535095293 is prime: True 115280095190773 is prime: True 115797848077099 is prime: True 1099726899285419 is prime: False real 0m2.745s user 0m2.703s sys 0m0.031s $ time python parallel-sample.py --process 112272535095293 is prime: True 112582705942171 is prime: True 112272535095293 is prime: True 115280095190773 is prime: True 115797848077099 is prime: True 1099726899285419 is prime: False real 0m0.983s user 0m3.984s sys 0m0.172s $ time python parallel-sample.py --thread 112272535095293 is prime: True 112582705942171 is prime: True 112272535095293 is prime: True 115280095190773 is prime: True 115797848077099 is prime: True 1099726899285419 is prime: False real 0m5.527s user 0m5.422s sys 0m0.109s
Le multi-processus accélère, mais le multi-thread ralentit.
F#
Port vers F #. Puisqu'il n'y a aucun problème à utiliser le multithreading dans le .NET Framework, nous omettons l'implémentation du multiprocessing.
parallel-sample.fsx
let PRIMES = [ 112272535095293L 112582705942171L 112272535095293L 115280095190773L 115797848077099L 1099726899285419L] let is_prime n = if n < 2L then false elif n = 2L then true elif n % 2L = 0L then false else let sqrt_n = int64 (floor (sqrt (float n))) seq { for i in {3L .. 2L .. sqrt_n + 1L} do if n % i = 0L then yield false yield true } |> Seq.head let is_prime_async n = async { return is_prime n } let main_parallel() = PRIMES |> Seq.map is_prime_async |> Async.Parallel |> Async.RunSynchronously |> Seq.zip PRIMES |> Seq.iter (fun (number, prime) -> printfn "%d is prime: %b" number prime) let main() = PRIMES |> Seq.map is_prime |> Seq.zip PRIMES |> Seq.iter (fun (number, prime) -> printfn "%d is prime: %b" number prime) match Array.last (System.Environment.GetCommandLineArgs()) with | "--thread" -> main_parallel() | _ -> main()
Résultat d'exécution
$ time mono parallel-sample.exe 112272535095293 is prime: true 112582705942171 is prime: true 112272535095293 is prime: true 115280095190773 is prime: true 115797848077099 is prime: true 1099726899285419 is prime: false real 0m0.662s user 0m0.625s sys 0m0.031s $ time mono parallel-sample.exe --thread 112272535095293 is prime: true 112582705942171 is prime: true 112272535095293 is prime: true 115280095190773 is prime: true 115797848077099 is prime: true 1099726899285419 is prime: false real 0m0.308s user 0m0.813s sys 0m0.078s
L'effet est difficile à voir à moins qu'il ne soit un peu plus lourd, mais la vitesse s'améliore.
Flux de processus
Il est facile de voir ce qui se passe avec ʻasync
et ʻAsync
en regardant les types.
Fonction asynchroniser
let is_prime n = ... // int64 -> bool let is_prime_async n = ... // int64 -> Async<bool>
Appliquer la liste des arguments
PRIMES |> Seq.map is_prime_async // seq<Async<bool>>
Combinez plusieurs processus asynchrones
|> Async.Parallel // Async<bool array>
Exécute un traitement asynchrone et renvoie le résultat sous forme de tableau
|> Async.RunSynchronously // bool array
Les calculs parallèles sont effectués dans le pool de threads. Même si vous passez un grand nombre de calculs à la fois, ils seront planifiés en conséquence.
Recommended Posts