Skip to content

very long run time and/or stack overflow during inference #7

New issue

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

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

Already on GitHub? # to your account

Closed
pylint-bot opened this issue Oct 18, 2013 · 13 comments
Closed

very long run time and/or stack overflow during inference #7

pylint-bot opened this issue Oct 18, 2013 · 13 comments
Labels

Comments

@pylint-bot
Copy link

Originally reported by: Buck Evan (BitBucket: bukzor, GitHub: @bukzor?)


This simple script generates a (admittedly perverse) class which, when analyzed causes astroid to stack overflow.

http://paste.pound-python.org/show/HvGNK3BPWVkbqY1VAlyl/

If the number of methods is somewhat reduced, a huge running time is incurred instead, which is (for me) even worse. I'm trying to measure it, but currently it's ten minutes and counting.

Here is a trace of the inference being performed at the time of stack overflow:
http://paste.pound-python.org/show/QEm5Q15I1WMrHGnrWyqU/

This is a real issue for us as the cheetah templating framework generates code equivalent to the above. This causes pylint to be unusable to check these generated files. I do want to check these generated python files for issues, and pylint seems like the most full-featured tool available.


@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


Profile of analysis of a similar class with just 14 methods, which takes 30 seconds.

@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


The raw data which was used to generate the above image.

1 similar comment
@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


The raw data which was used to generate the above image.

@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


As a minor update, my attempt to time the inference on the somewhat smaller class is still running, ten days later. The cputime is 9 days, 20:58:43.

I'm killing it now.

@pylint-bot
Copy link
Author

Original comment by Sylvain Thénault (BitBucket: sthenault, GitHub: @sthenault?):


there is much probably an infinite loop somewhere which doesn't end into an
infinite recursion error and so ends endlessly.

@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


My hypothesis is different: I believe it's tracing all the possible assignment paths in a permutative manner, yielding O(n^n) performance. With smaller numbers (3-10), it does complete, just exponentially slower for each new method.

@pylint-bot
Copy link
Author

Original comment by BitBucket: eevee, GitHub: @eevee?:


I looked into this a bit in the course of trying to improve pylint performance on a large and terrible codebase. :)

Buck is correct; this is equivalent to O(n^n). The following happens:

  • Encounter trans = self.transaction in method 1. Try to infer self.transaction.
  • Encounter self.transaction = trans in method 1. Infer trans. Find trans = self.transaction in method 1. Skip it because it's already being inferred.
  • Encounter self.transaction = trans in method 2. Infer trans. Find trans = self.transaction in method 2. Try to infer self.transaction.
  • Encounter self.transaction = trans in method 1...

This repeats rather ridiculously. Every assignment of self.transaction tries to infer from every assignment to it. Method 1 tries to infer from methods 2 through n, which each try to infer from methods 2 through n except themselves, etc. So each of n methods tries to infer from n - 1 methods which try to infer from n - 2 methods, and so on, producing O(n!). It only terminates at all because the inference of a particular expression is prevented from inferring itself; otherwise method 1 would try to infer itself forever.

I think I have a small fix for this, which involves extending the existing recursive-inference protection to avoid inferring the same attribute on an instance of the same class twice in the call stack. I'll post it as soon as I figure out how to do that with hg and bitbucket. :)

@pylint-bot
Copy link
Author

Original comment by Sylvain Thénault (BitBucket: sthenault, GitHub: @sthenault?):


sounds good eevee, thanks !

don't hesitate to ask for help for submitting your patch.

@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


+1 +1

@pylint-bot
Copy link
Author

Original comment by Buck Evan (BitBucket: bukzor, GitHub: @bukzor?):


@eevee Put your diff on http://paste.pound-python.org/ if you're really stuck on bitbucket/hg.

This page always gives me enough information to do what I need with hg: http://mercurial.selenic.com/wiki/GitConcepts#Command_equivalence_table

@pylint-bot
Copy link
Author

Original comment by Sylvain Thénault (BitBucket: sthenault, GitHub: @sthenault?):


@eevee any update on this? Astroid should be released soon, it would be great if it could include a fix for this one.

@pylint-bot
Copy link
Author

Original comment by BitBucket: eevee, GitHub: @eevee?:


Finally submitted pull request #28, which includes a commit to fix this issue.

@pylint-bot
Copy link
Author

Original comment by Sylvain Thénault (BitBucket: sthenault, GitHub: @sthenault?):


Avoid recursively inferring the same attr on the same class. Closes #7

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant