Skip to content
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

Unused closures in njitted function are wrongly reported as escaping #7254

Closed
2 tasks done
LunarLanding opened this issue Jul 27, 2021 · 5 comments · Fixed by #8782
Closed
2 tasks done

Unused closures in njitted function are wrongly reported as escaping #7254

LunarLanding opened this issue Jul 27, 2021 · 5 comments · Fixed by #8782
Labels
enhancement good first issue A good issue for a first time contributor Task

Comments

@LunarLanding
Copy link
Contributor

Reporting a bug

  • I have tried using the latest released version of Numba (most recent is
    visible in the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
  • I have included a self contained code sample to reproduce the problem.
    i.e. it's possible to run as 'python bug.py'.
    MVE:
import numba as nb
@nb.njit
def f(a,b):
    def g(b):
        return a+b
    
    return 1
print(nb.version_info)
f(1,2)
version_info(major=0, minor=53, patch=1, short=(0, 53), full=(0, 53, 1), string='0.53.1', tuple=('0', '53', '1'), git_revision=None)

---------------------------------------------------------------------------
TypingError                               Traceback (most recent call last)
/tmp/ipykernel_30576/270852093.py in <module>
      7     return 1
      8 print(nb.version_info)
----> 9 f(1,2)

~/miniconda3/lib/python3.9/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    418                 e.patch_message(msg)
    419 
--> 420             error_rewrite(e, 'typing')
    421         except errors.UnsupportedError as e:
    422             # Something unsupported is present in the user code, add help info

~/miniconda3/lib/python3.9/site-packages/numba/core/dispatcher.py in error_rewrite(e, issue_type)
    359                 raise e
    360             else:
--> 361                 raise e.with_traceback(None)
    362 
    363         argtypes = []

TypingError: Failed in nopython mode pipeline (step: convert make_function into JIT functions)
Cannot capture the non-constant value associated with variable 'a' in a function that will escape.

File "../../../../../../../tmp/ipykernel_30576/270852093.py", line 4:
<source missing, REPL/exec in use?>

@gmarkall
Copy link
Member

I can reproduce this. My intuition is that:

  • This will not be very easy to fix (it would require analysis to determine that g is dead code and elide the error message)
  • The error message is not entirely accurate - I do wonder if the problem is not that the function or its result might escape (it doesn't here), but that Numba doesn't compile inline functions with closure variables that are arguments to the outer jitted function.

@LunarLanding
Copy link
Contributor Author

Thanks for reproducing @gmarkall

If the function is used the error message does dissapear:

import numba as nb
@nb.njit
def f(a,x):
    def g(b):
        return a+b
    return g(x)
print(nb.version_info)
f(1,2)
version_info(major=0, minor=53, patch=1, short=(0, 53), full=(0, 53, 1), string='0.53.1', tuple=('0', '53', '1'), git_revision=None)
3

If the captured value is not an argument it still fails:

import numpy as np
import numba as nb
@nb.njit
def f(x):
    c = np.random.rand(1)
    def g(b):
        return c+b
    return 1
print(nb.version_info)
f(1)
version_info(major=0, minor=53, patch=1, short=(0, 53), full=(0, 53, 1), string='0.53.1', tuple=('0', '53', '1'), git_revision=None)

---------------------------------------------------------------------------
TypingError                               Traceback (most recent call last)
/tmp/ipykernel_2959/357528790.py in <module>
      8     return 1
      9 print(nb.version_info)
---> 10 f(1)

~/miniconda3/lib/python3.9/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    418                 e.patch_message(msg)
    419 
--> 420             error_rewrite(e, 'typing')
    421         except errors.UnsupportedError as e:
    422             # Something unsupported is present in the user code, add help info

~/miniconda3/lib/python3.9/site-packages/numba/core/dispatcher.py in error_rewrite(e, issue_type)
    359                 raise e
    360             else:
--> 361                 raise e.with_traceback(None)
    362 
    363         argtypes = []

TypingError: Failed in nopython mode pipeline (step: convert make_function into JIT functions)
Cannot capture the non-constant value associated with variable 'c' in a function that will escape.

File "../../../../../../../tmp/ipykernel_2959/357528790.py", line 6:
<source missing, REPL/exec in use?>

@stuartarchibald
Copy link
Contributor

I think the message perhaps ought to say "a function that may escape", as that covers the case of it not escaping. As to the above, any closed over variables must be compile time constants, calling np.random.rand(1) is not constant and using an argument from the outer function is also not constant.

@gmarkall gmarkall added good first issue A good issue for a first time contributor Task enhancement and removed needtriage labels Aug 2, 2021
@stuartarchibald
Copy link
Contributor

NOTE: The good first issue task is to alter the error message to read a function that may escape and to fix up any tests that fail as a result of the change.

@LunarLanding
Copy link
Contributor Author

using an argument from the outer function is also not constant.

I feel I need to clarify that this works(first example here ).
The issue is when this function is left unused.

So if I'm reading the replies correctly, when the inner function is used, in this case it is inlined, so it's directly referencing the argument variable. When it is not used, it's compiled, and an attempt is made to capture the argument variable, which fails because it is not a constant.

The context for leaving the function unused but in the source code is that closures are one way of capturing default arguments, and in long functions with many helpers, I found myself frequently having to comment out helpers. This however, in my specific case, with compiling taking 3mn.

I have since moved to using structrefs as a way to capture constant values with minimal overhead. This allows me to split functions without having to pass many arguments around. And the whole algorithm takes 1.5mn to compile, i.e. half of the original original time, and it's easy to incrementally modify it.

However, I'm not sure how one could prevent confusion that arises from first seeing no issues because the variable is being inlined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement good first issue A good issue for a first time contributor Task
Projects
Status: Timeboxed Issue tasks upto 1 hour
Development

Successfully merging a pull request may close this issue.

3 participants