====== Python: Multiprocessing et GIL ======
=====Python est lent et n'est pas multiprocessus=====
Balivernes ... billevesées ... \\
Toutes les Intelligences Artificielles ont une façade en python ... Dja dja (ah bon!)\\
L'interpréteur python est généralement CPython qui est comme son nom l'indique écrit en C, le C serait lent ?
=====GIL=====
* **https://realpython.com/python-gil/**
Le module threading permet de lancer des tâches en parallèles, mais en réalité elles ne le sont pas: tout se fait sur un seul cœur du processeur. Les tâches sont découpées en petits bouts, exécution d'un bout d'une, puis un petit de l'autre, etc ...
Le GIL (Global Interpreteur Lock) évite de surcharger un thread.
Le module multiprocessing utilise réellement un cœur pour chaque tâche, et il est possible que chaque tâche communique avec une autre.\\
Par contre, un processus n'utilise toujours qu'un seul cœur.
=====Ressources sur le module multiprocessing=====
* http://www.kasimte.com/multiprocessing-in-python-pool-process-queue-and-pipe
* https://stackoverflow.com/questions/11515944/how-to-use-multiprocessing-queue-in-python
* https://medium.com/analytics-vidhya/multithreading-and-multiprocessing-in-python-1f773d1d160d
* https://www.geeksforgeeks.org/multiprocessing-python-set-2/
Mais comme souvent, les tutos sont hyper-compliqués, les auteurs montrent qu'ils sont très forts avec des exemples inappropriés.
===Remarques===
Certains modules, tel que numpy, sont déjà multiprocess.
=====Exemples de Multiprocessing=====
Je vous propose des exemples simples:
"""
Exemple d'utilisation de multiprocessing
documentation
https://docs.python.org/3/library/multiprocessing.html
Exemple de 2 processus qui tournent en parallèle sans communiquer.
"""
import os
from multiprocessing import Process
from time import sleep
def first_processus():
print('first process:', os.getppid())
a = 0
while 1:
sleep(1)
a += 1
print("a =", a)
def second_processus():
print('second process:', os.getppid())
b = 0
while 1:
sleep(2)
b += 5
print("b =", b)
def main():
p = Process(target=second_processus)
p.start()
first_processus()
if __name__ == '__main__':
main()
"""
Exemple de 2 processus qui tournent en parallèle et échangent une variable avec Pipe.
https://docs.python.org/3/library/multiprocessing.html#exchanging-objects-between-processes
The Pipe() function returns a pair of connection objects connected
by a pipe which by default is duplex (two-way).
"""
import os
from multiprocessing import Process, Pipe
def test(n):
"""Fonction pour charger le processeur"""
for i in range(n):
m = (i ** 1.89999) ** 0.8974125
def first_processus(conn):
print('first process:', os.getppid())
a = 0
while 1:
test(1000000)
a += 1
print("a =", a)
b = conn.recv()
print("b dans first_process =", b)
a += b[1]
def second_processus(conn):
print('second process:', os.getppid())
b = 0
while 1:
test(2000000)
b += 5
print("b dans second process =", b)
conn.send(['b', b])
def main():
"""
https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Pipe
multiprocessing.Pipe([duplex])
If duplex is True (the default) then the pipe is bidirectional. If duplex is
False then the pipe is unidirectional: conn1 can only be used for receiving
messages and conn2 can only be used for sending messages.
"""
parent_conn, child_conn = Pipe()
p = Process(target=second_processus, args=(child_conn,))
p.start()
first_processus(parent_conn)
if __name__ == '__main__':
main()
=====Multiprocess dans kivy=====
Une application kivy qui excécute pyrealsense et coral dans un Process avec Pipe marche impeccable.
* **[[https://github.com/sergeLabo/personnages3d| sergeLabo personnages3d]]** Détection de 1 à 4 personnes
* Inferface graphique réalisée avec Kivy\\
* Suivi des personnes devant la caméra
=====multiprocessing.shared_memory =====
====Ressources====
* **[[https://docs.python.org/fr/3.9/library/multiprocessing.shared_memory.html|docs.python.org/fr Mémoire partagée en accès direct depuis plusieurs processus¶]]**
====Mon exemple à moi archi simple====
**Je n'ai pas besoin de montrer que je suis très fort pour me faire embaucher chez GAFAM.**
from time import time, sleep
import random
from multiprocessing import Process
from multiprocessing.sharedctypes import Value
class SharedMemory:
def __init__(self):
self.val = Value("i", -4000)
print(self.val, self.val.value)
my_proc = Process(target=another_process, args=(self.val, ))
my_proc.start()
def shared_memory_master(self):
t = time()
while time() - t < 4:
print(f"Lecture de {self.val}: {self.val.value}")
sleep(0.1)
def another_process(val):
t = time()
while time() - t < 3:
n = random.randint(-1000, 1000)
print(f"Maj de n = {n}")
val.value = n
sleep(0.3)
if __name__ == "__main__":
sh = SharedMemory()
sh.shared_memory_master()
====Exemple d'utilisation bas niveau d'instances de SharedMemory====
from multiprocessing import shared_memory
shm_a = shared_memory.SharedMemory(create=True, size=10)
m = shm_a.buf
print('type(m)', type(m))
buffer = shm_a.buf
print('len(buffer)', len(buffer))
# Modify multiple at once
buffer[:4] = bytearray([22, 33, 44, 55])
# Modify single byte at a time
buffer[4] = 100
# Attach to an existing shared memory block
shm_b = shared_memory.SharedMemory(shm_a.name)
import array
# Copy the data into a new array.array
ar = array.array('b', shm_b.buf[:5])
print('ar', ar)
# Modify via shm_b using bytes
shm_b.buf[:5] = b'howdy'
# Access via shm_a
print('bytes(shm_a.buf[:5])', bytes(shm_a.buf[:5]))
# Close each SharedMemory instance
shm_b.close()
shm_a.close()
# Call unlink only once to release the shared memory
shm_a.unlink()
====Exemple de partage de numpy array====
from multiprocessing import Process
from multiprocessing.managers import SharedMemoryManager
from multiprocessing.shared_memory import SharedMemory
import numpy as np
def test(shared_mem: SharedMemory, dtype):
a = np.frombuffer(shared_mem.buf, dtype=dtype)
a[0] = -a[0]
if __name__ == "__main__":
# Create the array
N = int(10)
unshared_arr = np.random.rand(N)
DTYPE = unshared_arr.dtype
with SharedMemoryManager() as smm:
shared_mem = smm.SharedMemory(size=unshared_arr.nbytes)
arr = np.frombuffer(shared_mem.buf, dtype=DTYPE)
arr[:] = unshared_arr
print("Originally, the first two elements of arr = %s" % (arr[:2]))
# Create, start, and finish the child processes
p = Process(target=test, args=(shared_mem, DTYPE))
p.start()
p.join()
# Printing out the changed values
print("Now, the first two elements of arr = %s" % arr[:2])
{{tag> kivy python sb }}