<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">2016-05-21 13:24 GMT+01:00 alessandro medici <span dir="ltr"><<a href="mailto:alexxandro.medici@gmail.com" target="_blank">alexxandro.medici@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote"><span class="">Il giorno 21 maggio 2016 12:51, enrico franchi <span dir="ltr"><<a href="mailto:enrico.franchi@gmail.com" target="_blank">enrico.franchi@gmail.com</a>></span> ha scritto:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">2016-05-20 13:59 GMT+01:00 alessandro medici <span dir="ltr"><<a href="mailto:alexxandro.medici@gmail.com" target="_blank">alexxandro.medici@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote></span><span><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
- "multiprocessing" implica (a meno di eccezioni notevoli) "pickle di<br>
tutto"<br></blockquote><div><br></div></span><div>? cioè i dati vengono trasmessi via pickle e non via puntatori? Sure?</div><div>O invece non ho capito cosa affermi? Sorry per la mia ignoranza, ma</div><div>sono anziano e con i capelli MOLTO grigi. </div></div></div></div></blockquote><div><br></div><div>Non e' questione di ignoranza, basta rifletterci. Allora... multi-*processing*.</div><div>Diciamo che e' sano assumere che siano piu' processi, ok? Quindi piu' processi hanno spazi di indirizzamento separati (visto che sono appunto processi multipli). Quindi... cosa se ne fa un processo di un puntatore a uno spazio di indirizzamento a cui non ha accesso? Diciamo nulla? Ah... ma c'e' la memoria condivisa... ecco, ora riflettiamo anche su questa cosa. Quando tu scrivi</div><div><br></div><div>foo = Bar()</div><div><br></div><div>tu stai dicendo a Python di creare un oggetto. Bene. Non gli stai dicendo dove crearlo. Il che vuole dire che non e' in memoria condivisa. L'alternativa sarebbe immaginare che Python ficcasse *tutto quanto* in memoria condivisa... </div></div></div></div></blockquote><div><br></div></span><div>Già. Ho passato buona parte di ieri a guardarmi proprio il funzionamento del Manager<br></div><div>dei processi. Ed ho lasciato a metà quello sul funzionamento di pickle.</div></div></div></div></blockquote><div><br></div><div>Il mio consiglio e' di lasciare perdere quella parte di multiprocessing (a dire il vero... io suggerirei di pensarci sopra 80 volte prima di fare roba vera con multiprocessing -- poi ne parliamo --.</div><div><br></div><div>Non vuoi usare qualcosa che sia scomodo e bug prone come la memoria condivisa, ma con le performance del message passing (anzi, sinceramente, con le performance da fanalino di coda del message passing, visto che altre implementazioni di concetti simili sono molto piu' efficienti).</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Il ragionamento che fai fila, però... Mettiamola così: mi piacerebbe che esistesse</div><div>un'opzione tale che permettesse di definire una variabile in uno spazio condiviso ed</div><div>accessibile direttamente dai singoli processi. Starebbe poi a me gestirla. E sicuramente</div><div>in molti casi sarebbe più veloce che impacchettare e spacchettare in giro dati che, magari,</div><div>servono solo in lettura. Poi, se voglio darmi da solo la zappa sui piedi, sono affari miei, in</div><div>pura logica Python :-)</div></div></div></div></blockquote><div><br></div><div>Si beh... come dire. Ora io capisco che a te piacerebbe questa feature. Io posso dirti che *credi* che ti piacerebbe. Nel 2016 praticamente tutti quelli che hanno architettura a memoria condivisa le stanno migrando, a parte in determinati casi specifici (e.g., embedded, kernel, possibilmente roba come pipeline audio e video, dove hai veramente tanti dati e copiarli diventa fastidioso).</div><div><br></div><div>Detto questo, sarebbe una feature assolutamente complicatissima da implementare e che costringerebbe a fare andare tutto come una lumaca e/o a rompere completamente l'astrazione di Python.</div><div><br></div><div>1. Python non ha il concetto di "area di memoria". Costruisci un oggetto. Python ci pensa. Qui tu dovresti in qualche modo dire a Python che un certo oggetto deve essere in un'area di memoria condivisa.</div><div><br></div><div>2. Come lo facciamo? O lo facciamo in modo implicito (che e' ancora meno banale) oppure inventiamo qualcosa tipo... boh</div><div><br></div><div>Foo.shared(...)</div><div><br></div><div>che ha la stessa semantica di un normale Foo(...) ma mette le cose in memoria condivisa.</div><div><br></div><div>Bene: nell'esatto istante in cui tu scrivi quella cosa, vuole dire che un bel po' di roba viene schiaffata in memoria condivisa. Per esempio l'oggetto classe Foo deve finirci. Ma non solo... ci deve finire qualunque oggetto nell'MRO di Foo. E stiamo cominciando a tirarci dietro parecchia roba.</div><div><br></div><div>Pero' aspetta... magari io avevo da qualche parte una roba come:</div><div><br></div><div>class Foo(object):</div><div>    def __init__(self):</div><div>        self.bar = Bar()</div><div><br></div><div>Ecco, l'oggetto che stiamo assegnando a self.bar deve pure finire anche lui in questa memoria condivisa. Lui, la sua classe e tutti i suoi antenati e tutti gli oggetti che entrano a fare parte dell'istanza (e cosi' ricorsivamente).</div><div><br></div><div>Ora... capisci che e' un bel po' di roba.</div><div><br></div><div>E capisci anche che praticamente *tutte* le operazioni su questi oggetti devono essere protette da un lock. Perche'? Beh... perche' se siamo in memoria condivisa vuole dire che piu' processi possono accedervi (o meglio... se li ho messi in memoria condivisa e' perche' voglio accederci da piu' processi).</div><div><br></div><div>Ecco... come ne usciamo? Abbiamo due soluzioni:</div><div>1. mettiamo un lock poco granulare sull'area di memoria </div><div>2. mettiamo lock granulari sugli oggetti</div><div><br></div><div>Bene... in un caso stiamo replicando, di fatto, i problemi del GIL anche a multi-processing. Nell'altro invece stiamo facendo la cosa che tutti quelli che hanno provato a togliere il GIL ci hanno detto essere lenta. In pratica abbiamo perso in partenza. E nota che la cosa e' drammatica: ogni volta che passi un oggetto ad una funzione (o che ritorni) devi agire sul reference count di quell'oggetto. Che e' diventata magicamente una variabile contesa fra piu' processi (e quindi o vai di lock oppure vai di CAS -- ma CAS funziona bene quando ci sono tante letture e poche scritture, in questo caso invece mi aspetto che siano bilanciate --). Insomma... inchiodi tipo tutto.</div><div><br></div><div>Capisci cosa intendo quando ti dico che non vuoi veramente questa cosa? Tra l'altro di fatto non conosco molti linguaggi di alto livello che ti consentono una granularita' tale sull'uso della memoria. E la memoria condivisa e' notoriamente una cattiva idea. E fa veramente a cazzotti con il memory model di un linguaggio ad oggetti.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class=""><div></div></span><div>Già e vediamo se ho capito, allora, e uso come esempio proprio il programmino nel sito che aggiorna il dizionario:</div><div><br></div><div>Lancio da processo padre il processo figlio, al quale vengono passati, pickled via Manager, i dati</div><div>dei parametri, poi quando il figlio arriva al punto dell'aggiornamento del dizionario il figlio</div><div>chiama il Manager che pickla i dati di nuovo, li torno al padre (sotto GIL e quindi in lista d'attesa</div><div>se vi sono altri processi che vogliono fare la stessa cosa) che aggiorna il</div><div>dizionario e torna il controllo, sempre via Manager al figlio. Corretto?</div></div></div></div></blockquote><div><br></div><div>Dimentica il GIL, in un certo senso. Cioe'... e' corretto se togli la questione del GIL, che in tutto questo e' quasi non coinvolto. Non e' esattamente cosi' per alcuni dettagli implementativi (ma non ho troppo voglia di scendere cosi' in dettaglio). Diciamo che in tutto questo il GIL non e' un problema; il "problema" se vuoi e' che appunto per "aggiornare memoria" bisogna spedirsi indietro oggetti potenzialmente cicciotti. Poi si trovano sempre scappatoie, eh... ma secondo me fra che non e' comodo e che non e' veloce potrebbe bastare dimenticarsene.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Se è così ora affronterei un multiprocesso con le idee molto più chiare sul cosa, quando, quanto</div><div>passare al processo stesso e sull'architettura che darei al soft.</div></div></div></div></blockquote></div><br>Ora  a me verrebbe da dire una cosa... se parli di "architettura", mi aspetto che vuoi fare le cose per bene. Probabilmente mettere queste cose in produzione.</div><div class="gmail_extra"><br></div><div class="gmail_extra">Ecco... io tenderei a tenermi lontano da multiprocessing (e dai thread). Diciamo che multiprocessing e' molto comodo se tipo devi buttare su uno script con un po' di concorrenza o magari provare a macinare un po' di dati in toolettino scientifico. </div><div class="gmail_extra"><br></div><div class="gmail_extra">Per il resto, e' un oggetto che riserva molte sorprese, e non del tipo divertente.</div><div class="gmail_extra"><br></div><div class="gmail_extra">Se hai un problema I/O bound guardati cose come gevent, tornado, il vecchio twisted o il nuovo asyncio. Se hai un problema CPU bound... boh, fai offload a chi puoi. Numpy in certi casi. Se no a volte con un po' di attenzione si possono fare cose belline con multiprocessing. Poi per dire l'ultima volta che lo ho messo in produzione mi sono scritto a mano tutte le primitive di comunicazione fra processi, perche' le implementazioni di default di multiprocessing non facevano per me (semantica, + tirano su thread, + sono lente... etc etc etc). Questo non vuole dire che per te non andranno bene: probabilmente se sono ancora li, vuole dire che vanno bene per la maggior parte delle persone.</div><div class="gmail_extra"><br clear="all"><div><br></div>-- <br><div class="gmail_signature"> .<br>..: -enrico-</div>
</div></div>