Bonjour, je suis tombé sur un sujet ou pour partager une liste en mémoire, il fallait la convertir en array:
import test
maliste=[1.0,2,5.4,9,10.2]
shared_array_base = list_of_list_to_array(ctypes.c_float, maliste)
with Pool(processes=None, initializer=_initialize_subprocess, initargs=(shared_array_base,)) as pool:
pool.starmap(StartProcess, [("process1")])
def _initialize_subprocess(shared_array_base):
"Initialise notre subprocess avec la mémoire partagée."
test.shared_array_base = shared_array_base
def StartProcess(name)
print(name)
print(test.shared_array_base)
def list_of_list_to_array(data_type, data):
return Array(data_type, [elt for row in data for elt in row], lock=False)
Mais pour des datetime comment on fait ? car le type date n'existe pas dans c_types.
Si, mais au risque de me répéter, tu vas sérialiser ces objets dans la zone mémoire à partager. L'autre processus peut donc lire ces données, mais pour les comprendre il devra les dé-sérialiser.
"Non, pickle permet aussi de stocker le résultat dans une chaîne de bytes "
Je doute que se soit une bonne idée, il vas falloir convertir la liste en chaine, et ensuite partager en mémoire cette chaine, sauf que ctype ne permet pas à ma connaissance de partager des bytes ?
Et ensuite, pour accéder a un élément de sa liste il devra nécessairement la reconvertir en liste, donc au final niveau consommation mémoire sa revient au même, et niveau temps d’accès c'est pire.
j'ai crois avoir enfin compris cette histoire de chaîne de caractère, mais par contre, j'ai une erreur d'aces en lecture sur ma variable !?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from multiprocessing import Pool, Array
from functools import partial
import ctypes
import numpy as np
from time import sleep
import pickle
import test
def heavy_duty_function(line):
shared_array = pickle.Unpickler(test.shared_array_base)
print(shared_array)
def _initialize_subprocess(shared_array_base):
test.shared_array_base = shared_array_base
def list_to_pickle(data):
return pickle.dumps(data)
if __name__=='__main__':
data = [0,1,2,3,4,5,6]
shared_array_base = list_to_pickle(data)
del data
with Pool(processes=4, initializer=_initialize_subprocess, initargs=(shared_array_base,)) as pool:
pool.map(heavy_duty_function, range(2))
dans la doc que j'ai trouvé c'est pourtant bien écrit qu'il faut utiliser la commande unpickle, j'ai essayé avec load, mais j'ai la meme erreur.
je me pose par contre la question de la synchronisation, comme la dit Slitaz, peut on lire/modifier un élément de la liste pickler sans la reconvertir en liste ?
Il faut réaliser que pour partager de la mémoire entre plusieurs processus avec Array, on abandonne le concept de la représentation d'un objet Python en mémoire pour quelque chose de beaucoup plus basique. Pour une liste d'int par exemple, on convertit les objets Python int en une représentation en mémoire, en utilisant le type ctypes.c_intpar exemple que l'on place les uns derrière les autres. Il faut donc manipuler des données plus basiques.
Pour un objet datetime, il n'y a pas en effet un équivalent ctypes. Il faut lui trouver une représentation. Et je te propose par exemple d'utiliser pickle pour transformer de manière unique cet objet python en une série d'octets (bytes). Je n'ai pas dit qu'il fallait sérialiser avec pickle l'entièreté de la liste ! Maintenant le problème est que tu dois t'assurer que chaque représentation de datetime prend le même espace mémoire, car sinon comment prendre un élément en particulier ?
En faisant un petit test chez moi, je trouve qu'avec le protocol par défaut de pickle, un objet datetime prend 44 octets. Donc si tu as 1000 objets, te voilà avec un espace mémoire de 44 000 octets. Tu peux accéder à n'importe quel élément avec des offsets de 44 octets. Tu prends ton objets de 44 octets, tu le reconvertit en objet datetime dans le nouveau processus avec pickle.loads(data) et tu manipules cet objets. Si tu as besoin de le ré-écrire, tu le dump de nouveau et tu le replaces dans l'espace alloué.
Donc c'est une conversion on-the-fly. Si tu ne lis qu'une valeur à la fois, ça ne prendra pas beaucoup de mémoire en plus. Par contre ce sera nécessairement un peu plus lent que si tu avais partager un type natif de c_type.
Mais une chose est certaine, c'est loin d'être trivial !
EDIT: si tu peux utiliser numpy, tu pourrais utiliser le type numpy.datetime64 qui prend 8 octets en mémoire et ne te demanderas donc pas toutes ces sérialisations.
non je voulais dire que c'était impossible, quand tu partage ta variable, tu ne peut le faire que au moment ou tu crée ton processus, tu pourra pas le faire avant.
shared_array_base doit obligatoirement être crée pendant la création de ton processus.
Moi je pense que c'est possible de séparer le pool et ton array, mais je n'ai pas les connaissance pour t'aider, peut être que quelqu'un d'autre pourra t'aider
Je ne pense pas que quelqu'un t'aideras, c'est un forum pour débutant, ton problème est assez complexe, tu ferais mieu de poster ton problème sur developpez.com ou stackoverflow. Tu auras des personnes plus compétente que nous ici.
Tu devrais peut-être nous expliquer quelles sont tes contraintes. De toute évidence il n'est pas possible de lancer un nouveau processus qui doit partager une zone mémoire sans lui indiquer qu'elle est cette zone... Par contre il n'est pas nécessaire que la zone contienne déjà des données. On pourrait imaginer que les sub-processes aient pour objectif de remplir cette zone mémoire, par exemple.
Tu veux partager ton array avec ... personne ?? C'est dur de partager quand on est tout seul.
Quand tu définis ta liste et que tu crées ton Array tu as une zone mémoire prête à être partagée. Tu peux appeler le bloc with que bien plus tard dans le code si nécessaire.
J'ai répondu à ça dans le 2ème paragraphe. Donc tu créés ton Array et par la suite quand tu créeras un nouveau processus avec with tu partageras ton Array.
entwanne — @entwanne — Un zeste de Python — La POO en Python — Notions de Python avancées — Les secrets d'un code pythonique