Skip to content

Add support for fp16 comparison native operators - #8010

Merged
sklam merged 16 commits into
numba:mainfrom
testhound:testhound/fp16-comparison-operators
Jul 27, 2022
Merged

sklam merged 16 commits into
numba:mainfrom
testhound:testhound/fp16-comparison-operators

Conversation

@testhound

Copy link
Copy Markdown
Contributor

This pull request adds support for the fp16 comparison operators (>, >=, <, <=, ==, !=). Note this PR depends on a not yet merged PR: #7556.

@gmarkall

Copy link
Copy Markdown
Member

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:

[False]
[ True]

i.e. the kernel seems to determine that 1 < 1.000001 is False. With the main branch the kernel evaluates the result to True. Am I doing anything incorrect in this experiment?

Comment thread numba/cuda/cudadecl.py Outdated

def _genfp16_comparison_operator(l_key):
@register_global(l_key)
class Cuda_fp16_cmp2(AbstractTemplate):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why the 2?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because it was determined in this PR: #7885
with input from @sklam that operator registration requires using 'AbstractTemplate' while intrinsics need to use 'ConcreteTemplate'.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread numba/cuda/cudadecl.py Outdated
Comment thread numba/cuda/cudadecl.py Outdated

@gmarkall gmarkall left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 float16 and other types (e.g. float32, int16, etc.) turn out?

There are also some comments and questions on the diff.

@gmarkall

Copy link
Copy Markdown
Member

gpuci run tests

@gmarkall gmarkall added 4 - Waiting on author Waiting for author to respond to review CUDA CUDA related issue/PR labels Apr 28, 2022
@testhound

Copy link
Copy Markdown
Contributor Author

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:

[False]
[ True]

i.e. the kernel seems to determine that 1 < 1.000001 is False. With the main branch the kernel evaluates the result to True. Am I doing anything incorrect in this experiment?

@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.

@testhound

Copy link
Copy Markdown
Contributor Author

gpuci run tests

@gmarkall gmarkall added 4 - Waiting on reviewer Waiting for reviewer to respond to author and removed 4 - Waiting on author Waiting for author to respond to review labels May 16, 2022
@testhound

Copy link
Copy Markdown
Contributor Author

gpuci run tests

Comment thread numba/cuda/cudadecl.py
def generic(self, args, kws):
assert not kws

if len(args) == 2 and \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 convertible being True?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 float16 is 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? :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 gmarkall left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 gmarkall added 4 - Waiting on author Waiting for author to respond to review and removed 4 - Waiting on reviewer Waiting for reviewer to respond to author labels May 19, 2022
@gmarkall gmarkall added this to the Numba 0.56 RC milestone May 19, 2022
@testhound

Copy link
Copy Markdown
Contributor Author

@gmarkall I updated the PR with all your requested changes.

@gmarkall gmarkall left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread numba/cuda/cudadecl.py
Comment on lines +397 to +403
# 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@gmarkall gmarkall self-assigned this Jul 19, 2022

@gmarkall gmarkall left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread numba/cuda/cudadecl.py Outdated
Use fleshed out comment description suggested by @gmarkall

Co-authored-by: Graham Markall <535640+gmarkall@users.noreply.github.com>
@testhound

Copy link
Copy Markdown
Contributor Author

gpuci run tests

@testhound

Copy link
Copy Markdown
Contributor Author

@gmarkall I like and agree with your more fully fleshed out comment and committed it.

@gmarkall gmarkall left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Many thanks for the update. I think this now looks good.

@esc / @stuartarchibald Could this have a CUDA buildfarm run please?

@gmarkall

Copy link
Copy Markdown
Member

(I think the CI issue here is the general one right now, not to do with this PR).

@gmarkall gmarkall added Pending BuildFarm For PRs that have been reviewed but pending a push through our buildfarm 4 - Waiting on CI Review etc done, waiting for CI to finish and removed 4 - Waiting on reviewer Waiting for reviewer to respond to author labels Jul 21, 2022
@esc

esc commented Jul 25, 2022

Copy link
Copy Markdown
Member

(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

@esc

esc commented Jul 25, 2022

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines successfully started running 1 pipeline(s).

@gmarkall

Copy link
Copy Markdown
Member

gpuci run tests

@esc

esc commented Jul 25, 2022

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines successfully started running 1 pipeline(s).

@stuartarchibald

Copy link
Copy Markdown
Contributor

Buildfarm ID: numba_smoketest_cuda_yaml_144.

@stuartarchibald

Copy link
Copy Markdown
Contributor

Buildfarm ID: numba_smoketest_cuda_yaml_144.

Passed.

@stuartarchibald stuartarchibald added BuildFarm Passed For PRs that have been through the buildfarm and passed 5 - Ready to merge Review and testing done, is ready to merge and removed Pending BuildFarm For PRs that have been reviewed but pending a push through our buildfarm 4 - Waiting on CI Review etc done, waiting for CI to finish labels Jul 27, 2022
@sklam
sklam merged commit 09ca9dc into numba:main Jul 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to merge Review and testing done, is ready to merge BuildFarm Passed For PRs that have been through the buildfarm and passed CUDA CUDA related issue/PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants