Skip to content

Conversation

@dalcinl
Copy link
Contributor Author

dalcinl commented Oct 3, 2017

@agronholm As you mentioned you are busy, here you have a reproducer with output from runs before and after the changes in this PR. Note how the count (and obviously memory footprint) of bytearray instances keeps going up while you iterate over as_completed() and Executor.map() iterators.

Reproducer script:

# file: reproducer.py
import gc
import time
from pympler import summary
from pympler import muppy

from concurrent.futures import ThreadPoolExecutor, as_completed


def random_data(*args):
    time.sleep(1)
    return bytearray(1024*1024*2)


def gen_executor0():
    with ThreadPoolExecutor(1) as executor:
        futures = [executor.submit(random_data) for x in range(5)]
        for future in as_completed(futures):
            futures.remove(future)
            future = None
            yield

def gen_executor1():
    with ThreadPoolExecutor(1) as executor:
        for y in executor.map(random_data, range(5)):
            y = None
            yield 

def main(gen_executor):
    for cycle in gen_executor():
        gc.collect()
        summary.print_(summary.summarize(muppy.get_objects()), limit=4)

if __name__ == '__main__':
    print("as_completed()")
    main(gen_executor0)
    print("executor.map()")
    main(gen_executor1)

Run before this PR:

$ python reproducer.py
as_completed()
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           1 |      2.00 MB
       dict |         767 |      1.14 MB
        str |        8468 |    803.97 KB
       code |        2595 |    324.38 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           2 |      4.00 MB
       dict |         768 |      1.14 MB
        str |        8468 |    803.97 KB
       code |        2595 |    324.38 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           3 |      6.00 MB
       dict |         767 |      1.14 MB
        str |        8468 |    803.97 KB
       code |        2595 |    324.38 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           4 |      8.00 MB
       dict |         766 |      1.14 MB
        str |        8468 |    803.97 KB
       code |        2595 |    324.38 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           5 |     10.00 MB
       dict |         765 |      1.14 MB
        str |        8466 |    803.89 KB
       code |        2595 |    324.38 KB
executor.map()
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           1 |      2.00 MB
       dict |         766 |      1.14 MB
        str |        8465 |    803.84 KB
       code |        2596 |    324.50 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           2 |      4.00 MB
       dict |         765 |      1.14 MB
        str |        8465 |    803.84 KB
       code |        2596 |    324.50 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           3 |      6.00 MB
       dict |         764 |      1.14 MB
        str |        8465 |    803.84 KB
       code |        2596 |    324.50 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           4 |      8.00 MB
       dict |         763 |      1.14 MB
        str |        8465 |    803.84 KB
       code |        2596 |    324.50 KB
      types |   # objects |   total size
=========== | =========== | ============
  bytearray |           5 |     10.00 MB
       dict |         762 |      1.14 MB
        str |        8463 |    803.76 KB
       code |        2596 |    324.50 KB

Run after this PR:

$ python reproducer.py
as_completed()
  types |   # objects |   total size
======= | =========== | ============
   dict |         765 |      1.14 MB
    str |        8470 |    804.44 KB
   code |        2596 |    324.50 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         764 |      1.14 MB
    str |        8470 |    804.44 KB
   code |        2596 |    324.50 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         761 |      1.14 MB
    str |        8470 |    804.44 KB
   code |        2596 |    324.50 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         758 |      1.13 MB
    str |        8470 |    804.44 KB
   code |        2596 |    324.50 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         755 |      1.13 MB
    str |        8461 |    804.04 KB
   code |        2596 |    324.50 KB
   type |         167 |    148.64 KB
executor.map()
  types |   # objects |   total size
======= | =========== | ============
   dict |         764 |      1.14 MB
    str |        8467 |    804.30 KB
   code |        2597 |    324.62 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         761 |      1.14 MB
    str |        8467 |    804.30 KB
   code |        2597 |    324.62 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         758 |      1.13 MB
    str |        8467 |    804.30 KB
   code |        2597 |    324.62 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         755 |      1.13 MB
    str |        8467 |    804.30 KB
   code |        2597 |    324.62 KB
   type |         167 |    148.64 KB
  types |   # objects |   total size
======= | =========== | ============
   dict |         752 |      1.13 MB
    str |        8458 |    803.91 KB
   code |        2597 |    324.62 KB
   type |         167 |    148.64 KB

Copy link
Owner

@agronholm agronholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two oddities, otherwise looks good.

yield future
for f in _yield_finished_futures(finished, waiter,
ref_collect=(fs,)):
f = [f]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this? Why not just yield f?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh...because otherwise the future would still be referenced by the generator. Gotcha.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Maybe Python 4 will consider fixing iteration target vars leaking to the scope :-).

finished.reverse()
for f in _yield_finished_futures(finished, waiter,
ref_collect=(fs, pending)):
f = [f]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

@agronholm agronholm merged commit 28fa404 into agronholm:master Nov 29, 2017
@agronholm
Copy link
Owner

Thank you very much!

@dalcinl dalcinl deleted the bpo-27144 branch November 29, 2017 19:55
agronholm added a commit that referenced this pull request Nov 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants