Skip to content

BUG: deallocate recursive closure in arrayprint.py #10621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 19, 2018

Conversation

ahaldane
Copy link
Member

Fixes #10620

@ahaldane
Copy link
Member Author

Hmm, my original code here to do del recurse (possibly with gc.collect()) doesn't work.

I have to admit, I don't understand PyPy vs CPython garrbage collection well enough to see how to cause the closure to get collected immediately.

So I'm just going to go for the code reorg here to avoid the closure. All I did is move the closure out to its own function, with a new arg to store the closure variables.

@charris charris removed the 09 - Backport-Candidate PRs tagged should be backported label Feb 18, 2018
"""formatArray is designed for two modes of operation:
"param" are the values that are invariant under recursion, including
the array to be printed itself. index, indent and curr_width
are updated during recursion.
Copy link
Member

Choose a reason for hiding this comment

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

A full up documentation string would be helpful here. I didn't worry about that for the backport, but it would be good to have it going forward.

@eric-wieser
Copy link
Member

eric-wieser commented Feb 18, 2018 via email

finally:
# recursive closures have a cyclic reference to themselves, which
# requires gc to collect (gh-10620). To avoid this problem, for
# performance and PyPy friendliness, we break the cycle:
Copy link
Member

Choose a reason for hiding this comment

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

Also to avoid UPDATE_IF_COPY interactions, but no need to mention that here

# recursive closures have a cyclic reference to themselves, which
# requires gc to collect (gh-10620). To avoid this problem, for
# performance and PyPy friendliness, we break the cycle:
recurser = None
Copy link
Member

Choose a reason for hiding this comment

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

Should be block_recursion = None?

Copy link
Member Author

Choose a reason for hiding this comment

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

whoops!

@ahaldane ahaldane force-pushed the fix_arrayprint_recursive_closure branch from e667c0e to 50fde71 Compare February 19, 2018 04:22
@ahaldane
Copy link
Member Author

Fixed up.

Copy link
Member

@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

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

LGTM pending tests

@eric-wieser
Copy link
Member

Out of curiousity - does using a Y-combinator solve the reference cycle?

@ahaldane
Copy link
Member Author

Ah, that's a bit over my head, though I would be pleased to understand that on the tips of my fingers. I thought monads were the way of storing hidden state (closure variables).

@eric-wieser eric-wieser merged commit 71555c4 into numpy:master Feb 19, 2018
@eric-wieser
Copy link
Member

eric-wieser commented Feb 19, 2018

I guess the key thing here is to get the self-reference via an argument rather than a closure. Without invoking a full y-combinator:

def recurser(recurser, outer_args):
    ...
    # do the recursive call
    recurser(recurser, inner_args)

recurser(recurser, initial_args)

Or with one:

def fix(f):
    return lambda *args, **kwargs: f(fix(f), *args, **kwargs)

@fix
def recurser(recurser, outer_args):
    ...
    # do the recursive call
    recurser(inner_args)

recurser(initial_args)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants