-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Allow (compile-time) conditional "with nogil" blocks #2579
Comments
@scoder For example:
I wonder if it's possible to make a solution such as
Another thing is that the schedule strategy of prange also has to be decided at compile time and it would be nice to be able to decide it at runtime. |
With prange/OpenMP, you can set the thread count to 1 or configure the schedule with the |
@scoder, I would like to try to take this task on and I would like to hear if you think I'm on the right track. Boundcheck is first identified as a function used as a decorator but in the InterpretCompilerDirectives it is transform into a CompilerDirectivesNode (which contains the decorated function). Before creating the CompilerDirectivesNode, the expression used as the boundscheck argument is verified to be a BoolNode (so either True or False, without any need to evaluated the expression). I imagine GILStatNode will have to be modified to accept an optional condition (an ExprNode).
If a condition is provided, e.g.
Then the condition is stored as the expression of the condition of the GILStatNode. A later transformation (after the fused type function is "unpacked" into multiple functions in the AnalyseDeclarationsTransform phase) will have to check whether the condition is a valid condition (either a constant boolean or an expression such as "numeric is int". The logic to check for expressions such as "numeric is int" already exist in the ReplaceFusedTypeChecks visitor which is also called during the AnalyseDeclarationsTransform phase). ReplaceFusedTypeChecks replaces conditions such as "numeric is int" with a BoolNode (True / False) which I believe is pruned later (the IfClauseNode is removed along with it's body if it the condition is False, or the IfClauseNode is removed but the body remains if the condition is True). Perhaps ReplaceFusedTypeChecks can also be used to replace the condition of the GILStatNode with True / False. An alternative is to create another Node to represent the conditional nogil. What do you think? |
Perfect analysis. The evaluation of the condition is already implemented, so all the Note that Now, the final question is whether all of this happens in the right order in the compiler pipeline. It might be that we already need to know about All positive tests for this should go into a single new test file under I hope I answered all your questions so far. Keep asking if you need more help. |
Would it be necessary to discard GILStatNodes outside of non fused types functions? |
They somehow have to do nothing when disabled, and I think that discarding them is the easiest way to do that. Fused functions are not the only use case for this feature. You could also just imagine a global |
should I use a compile time expression or a (normal) expression as the condition of the gil/nogil statement ? |
Anything constant ( |
Should that occur in the c'tor of GILStateNode, during parsing (in Parsing.py -> p_with_items), or in the GILCheck transform? |
I'd actually do it in the |
So during Parsing I should use |
No, |
In If they are not currently optimized (using |
Or does the base class of |
Okay, I just saw the line
So I should definitely have the |
There is a default handler for the node base class |
should the children of This question I believe is relevant to all visitors that visit GILStatNode. |
When you add a new attribute, you have to add it to the |
@scoder do you have an idea how I can write a test which checks whether the gil is taken? I guess I can write a test which will fail if the gil is not taken (using some PyObject method for example). |
Yes, I think a test that uses some arbitrary Python operations in |
Especially when using fused types, some code section may or may not require the GIL. Currently, the only way to release the GIL in these cases is to duplicate the code sections and wrap one in a
with nogil
block but not the other, e.g.The normal reflex of moving the code into a cdef function also does not work out because the function then has the same declaration problem.
At least for cases where the distinction is known at compile time (which includes type checks for fused types), there should be a way to base the
nogil
case on a condition.Constraints:
The solution should be as obvious from a Python perspective as possible.
with nogil
is currently special-cased in Cython, but that shouldn't impact its use. An obvious approach in Python would be, for example:but this is difficult to match with the need for special casing
nogil
in Cython at compile time.Also, while an initial implementation could be restricted to compile time constant conditions, the same syntax should still be applicable when allowing the decision to be taken at runtime (by implicitly generating a duplicate code block in C).
nogil
is not only a (special) context manager in Cython but also a decorator, sonogil(True)
might not be easy to distinguish from@nogil
, especially in pure Python code (Shadow.py
). Still, also allowing@nogil(True)
as a decorator would be nice.Approach:
Given that directives like
with boundscheck(True)
already exist,with nogil(True)
is probably the way to go. Thus, allowwith nogil(flag)
and detect the decorator case in pure Python mode by checking for callables in the dummy implementation inShadow.py
.The
InterpretCompilerDirectives
transform in ParseTreeTransforms.py is the place where compiler directives are evaluated. Forwith nogil
andwith gil
, aNodes.GILStatNode
is created. Note that this is run before transforming normal with-statements (with is not relevant for this special case) and constant folding (in theConstantFolding
tree transform, which determines the compile time value of the condition), and also long before type analysis (AnalyseExpressionsTransform
) where fused types are evaluated (see the order in Pipeline.py), so the directive condition will not be finally known until constant folding and type analysis have run, and must therefore be stored as a syntax tree node instead of a boolean flag.The type analysis step should be able to take the final decision about the condition value in
Nodes.GILStatNode
. However, it could well be that certain syntax tree nodes in the code block have already decided by then that they are in anogil
context. That might need to be revised to make sure they only take that decision after the compile time condition is evaluated and the final GIL state known.The text was updated successfully, but these errors were encountered: