<div dir="ltr"><div>Is your concern something like the following?</div><div><br></div>with Pool(8) as p:<div>    gen = p.imap_unordered(func, ls)</div><div>    first_elem = next(gen)</div><div>    p.apply_async(long_func, x)</div><div>    remaining_elems = [elem for elem in gen]</div><div><br></div><div>...here, if we store "func" on each worker Process as a global, and execute this pattern above, we will likely alter state of one of the worker processes s.t. it stores "long_func" in place of the initial "func".  </div><div><br></div><div>So yes, this could break things. <u>A potential solution</u>:</div><div><br></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Replace "func" in the task tuple with an identifier (maybe, <i>perhaps naively</i>, func.__qualname__), and store the "identifier => func map" somewhere globally accessible, maybe as a class attribute on Pool. On any call to Pool.map, Pool.apply, etc... this map is updated. Then, in the worker process, as each task is processed, we use the "func identifier" on the task to recover the globally mapped 'func', and apply it.</blockquote><div> </div><div>This would avoid the weird stateful bug above. We could also do something slightly different to this if folks are averse to the "Pool class-attribute func map" (i.e. averse to globals), and store this map as an Instance Attribute on the Pool object, and wrap the "initializer" func to make the map globally available in the worker via the "global" keyword.</div></div><div><br></div><div>One note: this isn't a "cache", it's just a global map which has its keys & values updated <u>blindly</u> with every call to Pool.<public_method>. It serves as a way to bypass repeated serialization of functions in Pool, which can be large when bound to big objects (like large class instances, or functools.partial objects).</div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, Oct 16, 2018 at 9:27 AM Michael Selik <<a href="mailto:michael.selik@gmail.com">michael.selik@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto">Would this change the other pool method behavior in some way if the user, for whatever reason, mixed techniques?<div dir="auto"><br></div><div dir="auto">imap_unordered will only block when nexting the generator. If the user mingles nexting that generator with, say, apply_async, could the change you're proposing have some side-effect?</div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, Oct 16, 2018, 5:09 AM Sean Harrington <<a href="mailto:seanharr11@gmail.com" target="_blank">seanharr11@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">@Nataniel this is what I am suggesting as well. No cacheing - just storing the `fn` on each worker, rather than pickling it for each item in our iterable.<br><div><br></div><div>As long as we store the `fn` post-fork on the worker process (perhaps as global), subsequent calls to Pool.map shouldn't be effected (referencing Antoine's & Michael's points that "multiprocessing encapsulates each subprocesses globals in a separate namespace").</div><div><br></div><div>@Antoine - I'm making an effort to take everything you've said into consideration here.  My initial PR <a href="https://www.youtube.com/watch?v=DH0JVSXvxu0" rel="noreferrer" target="_blank">and talk</a> was intended to shed light on a couple of pitfalls that I often see Python end-users encounter with Pool. Moving beyond my naive first attempt, and the onslaught of deserved criticism, it seems that we have an opportunity here: No changes to the interface, just an optimization to reduce the frequency of pickling.</div><div><br></div><div>Raymond Hettinger may also be interested in this optimization, as he speaks (with great analogies) about <a href="https://www.youtube.com/watch?v=9zinZmE3Ogk" rel="noreferrer" target="_blank">different ways you can misuse concurrency in Python</a>. This would address one of the pitfalls that he outlines: the "size of the serialized/deserialized data".</div><div><br></div><div><div>Is this an optimization that either of you would be willing to review, and accept, if I find there is a *reasonable way* to implement it?</div><br class="m_-1253039868326851032m_2888957571148671746gmail-Apple-interchange-newline"></div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Oct 12, 2018 at 3:40 PM Nathaniel Smith <<a href="mailto:njs@pobox.com" rel="noreferrer" target="_blank">njs@pobox.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div><div class="gmail_quote"><div dir="ltr">On Fri, Oct 12, 2018, 06:09 Antoine Pitrou <<a href="mailto:solipsis@pitrou.net" rel="noreferrer" target="_blank">solipsis@pitrou.net</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Fri, 12 Oct 2018 08:33:32 -0400<br>
Sean Harrington <<a href="mailto:seanharr11@gmail.com" rel="noreferrer noreferrer" target="_blank">seanharr11@gmail.com</a>> wrote:<br>
> Hi Nathaniel - this if this solution can be made performant, than I would<br>
> be more than satisfied.<br>
> <br>
> I think this would require removing "func" from the "task tuple", and<br>
> storing the "func" "once per worker" somewhere globally (maybe a class<br>
> attribute set post-fork?).<br>
> <br>
> This also has the beneficial outcome of increasing general performance of<br>
> Pool.map and friends. I've seen MANY folks across the interwebs doing<br>
> things like passing instance methods to map, resulting in "big" tasks, and<br>
> slower-than-sequential parallelized code. Parallelizing "instance methods"<br>
> by passing them to map, w/o needing to wrangle with staticmethods and<br>
> globals, would be a GREAT feature! It'd just be as easy as:<br>
> <br>
>     Pool.map(self.func, ls)<br>
> <br>
> What do you think about this idea? This is something I'd be able to take<br>
> on, assuming I get a few core dev blessings...<br>
<br>
Well, I'm not sure how it would work, so it's difficult to give an<br>
opinion.  How do you plan to avoid passing "self"?  By caching (by<br>
equality? by identity?)?  Something else?  But what happens if "self"<br>
changed value (in the case of a mutable object) in the parent?  Do you<br>
keep using the stale version in the child?  That would break<br>
compatibility...<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">I was just suggesting that within a single call to Pool.map, it would be reasonable optimization to only send the fn once to each worker. So e.g. if you have 5 workers and 1000 items, you'd only pickle fn 5 times, rather than 1000 times like we do now. I wouldn't want to get any fancier than that with caching data between different map calls or anything.</div><div dir="auto"><br></div><div dir="auto">Of course even this may turn out to be too complicated to implement in a reasonable way, since it would require managing some extra state on the workers. But semantically it would be purely an optimization of current semantics.</div><div dir="auto"><br></div><div dir="auto">-n</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
</blockquote></div></div></div>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" rel="noreferrer" target="_blank">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/seanharr11%40gmail.com" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/seanharr11%40gmail.com</a><br>
</blockquote></div>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" rel="noreferrer" target="_blank">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/mike%40selik.org" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/mike%40selik.org</a><br>
</blockquote></div>
</blockquote></div>