Repository navigation
Add support for fp16 comparison native operators - #8010
Conversation
|
The following: from numba import cuda
import numpy as np
@cuda.jit
def f(r, x ,y):
i = cuda.grid(1)
r[i] = x[i] < y[i]
N = 1
x = np.ones(N, dtype=np.float16)
y = np.ones(N, dtype=np.float32) + 1.0e-6
r = np.zeros(N, dtype=np.bool)
f[1, N](r, x, y)
print(r)
print(x < y)prints: i.e. the kernel seems to determine that |
|
|
||
| def _genfp16_comparison_operator(l_key): | ||
| @register_global(l_key) | ||
| class Cuda_fp16_cmp2(AbstractTemplate): |
There was a problem hiding this comment.
To clarify my thinking - I think the name Cuda_fp16_cmp2 is a little confusing - there is the other class in the preceding method, but there's no name collision so it wouldn't have been necessary to call it ...cmp2 as opposed to ..cmp - the difference between this class and the one from the preceding method is that this one is for typing comparisons using the operator module, so naming it Cuda_fp16_operator_cmp (for example) would be more descriptive.
gmarkall
left a comment
There was a problem hiding this comment.
I've had a quick pass over this. In addition to the issue where some comparisons appear to evaluate incorrectly (in my previous comment), I think the testing is a bit too rudimentary:
- What happens if there are multiple operator comparisons in a single kernel / function?
- How do comparisons between
float16and other types (e.g.float32,int16, etc.) turn out?
There are also some comments and questions on the diff.
|
gpuci run tests |
@gmarkall based on conversations with @sklam about argument promotion we determined that '1.000001' is not representable in float16. We also determined that the code should have been promoted to float32 (wider mode). I will update the code when I return from vacation. |
…ators and add additional test cases
|
gpuci run tests |
|
gpuci run tests |
| def generic(self, args, kws): | ||
| assert not kws | ||
|
|
||
| if len(args) == 2 and \ |
There was a problem hiding this comment.
Can you explain the thinking behind the logic here? Some questions that spring to mind when I read this implementation:
- Why not just explicitly require both arguments to be float16? If they are convertible, won't a cast be inserted by typing?
- What types are convertible to float16 safely that would result in
convertiblebeingTrue?
There was a problem hiding this comment.
Further to this, I notice that the following implementation passes all tests:
def generic(self, args, kws):
assert not kws
if args == (types.float16, types.float16):
return signature(types.b1, types.float16, types.float16)Does this imply that there is some missing test coverage, or that the simple implementation suffices? (my suspicion is that the simple implementation suffices, but I don't think I've understood yet why the more complex implementation could be needed, so I'm not certain either way).
There was a problem hiding this comment.
@gmarkall requiring both arguments to be float16 will fail, forcing one argument to be float16 ended up generating incorrect mixed type code. As an example comparing a float16 and float32 converted the float32 to float16 and then doing the comparison which is incorrect. The types int16 and int8 can be converted to float16.
There was a problem hiding this comment.
Thankyou for the information - however, I don't really understand the explanation. I have the following patch applied on top of your commit d9abed6:
diff --git a/numba/cuda/cudadecl.py b/numba/cuda/cudadecl.py
index c0ef42652..a54740efd 100644
--- a/numba/cuda/cudadecl.py
+++ b/numba/cuda/cudadecl.py
@@ -5,7 +5,6 @@ from numba.core.typing.templates import (AttributeTemplate, ConcreteTemplate,
AbstractTemplate, CallableTemplate,
signature, Registry)
from numba.cuda.types import dim3, grid_group
-from numba.core.typeconv import Conversion
from numba import cuda
import operator
@@ -374,17 +373,8 @@ def _genfp16_comparison_operator(l_key):
def generic(self, args, kws):
assert not kws
-
- if len(args) == 2 and \
- (args[0] == types.float16 or args[1] == types.float16):
- if (args[0] == types.float16):
- convertible = self.context.can_convert(args[1], args[0])
- else:
- convertible = self.context.can_convert(args[0], args[1])
-
- if (convertible == Conversion.exact) or \
- (convertible == Conversion.promote):
- return signature(types.b1, types.float16, types.float16)
+ if args == (types.float16, types.float16):
+ return signature(types.b1, types.float16, types.float16)
Cuda_hadd = _genfp16_binary(cuda.fp16.hadd)I run the testsuite like this:
python -m numba.runtests numba.cuda.tests -m
and I get the following summary as a result:
----------------------------------------------------------------------
Ran 1295 tests in 115.148s
OK (skipped=18, expected failures=8)
From this I can see three possibilities:
- The implementation could just require both arguments to be
float16, and everything will still work. If this is the case, then please could you change to the simple implementation outlined above. - Requiring both arguments to be
float16is in fact incorrect, but there is no test that demonstrates the need for the more complex implementation in this PR. If this is the case, could you please add a test that will pass with the implementation in this PR, and fail with the simple implementation outlined above. - There's something else going on that I don't understand - if this is the case, could you please help me to understand where I'm going wrong? :-)
There was a problem hiding this comment.
@gmarkall I added two test cases that verify the generated ptx. One of these test cases fails when using your code that requires both arguments to be float16.
There was a problem hiding this comment.
Thanks - this now makes sense. I now recall that the complexity here is to work around the fact that we can't have multiple concrete templates for a single global. I think what's going on here is going to be quite unclear to anyone coming across this for the first time - can you please add some comments explaining the background and how the workaround works to the Cuda_fp16_operator_cmp class?
gmarkall
left a comment
There was a problem hiding this comment.
Many thanks for the update - I note that the changes are relatively small on top of #7556, so there is just one area commented on the diff - my suspicion is that the typing implementation could be simplified. I think everything else looks OK.
|
@gmarkall I updated the PR with all your requested changes. |
gmarkall
left a comment
There was a problem hiding this comment.
Many thanks for the updates. There are a couple of comments from the last review that don't appear to be addressed / responded to (I've marked as resolved all the ones I can see). I also think the commentary explaining why we need a ConcreteTemplate is still needed - there's a comment on the diff going into a little more detail there too.
| # We allow three cases here: | ||
| # | ||
| # 1. Comparing fp16 to fp16 - Conversion.exact | ||
| # 2. Comparing fp16 to types fp16 can be promoted to | ||
| # - Conversion.promote | ||
| # 3. Comparing fp16 to int8 (safe conversion) - | ||
| # - Conversion.safe |
There was a problem hiding this comment.
This does explain what conversions we allow based on the type-conversion rules. However, the question in most readers' minds (I imagine) when encountering this typing function, will by why we need an AbstractTemplate with what appears to be a partial re-implementation of Numba's casting logic, instead of just a ConcreteTemplate with a list of cases.
It took me some time to grasp this, and our discussions in the other comments (e.g. above) seem to cover the reasoning. Could you please extend the commentary to explain why we go to all this trouble with the ConcreteTemplate?
gmarkall
left a comment
There was a problem hiding this comment.
Many thanks for the updates. There is one remaining comment / suggestion about the description of the workaround for typing - after that is resolved, I think this should be good to approve.
Use fleshed out comment description suggested by @gmarkall Co-authored-by: Graham Markall <535640+gmarkall@users.noreply.github.com>
|
gpuci run tests |
|
@gmarkall I like and agree with your more fully fleshed out comment and committed it. |
gmarkall
left a comment
There was a problem hiding this comment.
Many thanks for the update. I think this now looks good.
@esc / @stuartarchibald Could this have a CUDA buildfarm run please?
|
(I think the CI issue here is the general one right now, not to do with this PR). |
This is (was) correct and was related to an outage on anaconda.org |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
gpuci run tests |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Buildfarm ID: |
Passed. |
This pull request adds support for the fp16 comparison operators (>, >=, <, <=, ==, !=). Note this PR depends on a not yet merged PR: #7556.