Repository: numba/numba

Issue:
Create a way to either check `parallel` worked, or warn if it was specified and it failed
This ticket https://github.com/numba/numba/issues/2460 has a valid sample that doesn't have much performance change under the `parallel` target likely due to lack of work. It raises the question more generally of:

> If a user has specified `parallel=True` how does the user know if the function was able to be transformed and executed in parallel, given fall back to `njit` scalar execution is silent.

Options to resolve this:

1. Provide a function to check if a function will execute using the parallel scheduler.
2. Add a warning if `parallel=True` is requested but the function could not be transformed for parallel execution.
3. Both of the above.


Discussion:
I like both, since option 1 is handy for unit tests, but option 2 is better for users

Resolving patch:
diff --git a/numba/compiler.py b/numba/compiler.py
--- a/numba/compiler.py
+++ b/numba/compiler.py
@@ -16,7 +16,7 @@
                    errors, types, ir, types, rewrites, transforms)
 from numba.targets import cpu, callconv
 from numba.annotations import type_annotations
-from numba.parfor import ParforPass
+from numba.parfor import ParforPass, Parfor
 from numba.inline_closurecall import InlineClosureCallPass
 
 
@@ -493,6 +493,29 @@ def stage_parfor_pass(self):
             self.type_annotation.calltypes, self.return_type, self.typingctx)
         parfor_pass.run()
 
+        if config.WARNINGS:
+            # check the parfor pass worked and warn if it didn't
+            has_parfor = False
+            for blk in self.func_ir.blocks.values():
+                for stmnt in blk.body:
+                    if isinstance(stmnt, Parfor):
+                        has_parfor = True
+                        break
+                else:
+                    continue
+                break
+
+            if not has_parfor:
+                # parfor calls the compiler chain again with a string
+                if not self.func_ir.loc.filename == '<string>':
+                    msg = ("parallel=True was specified but no transformation"
+                           " for parallel execution was possible.")
+                    warnings.warn_explicit(msg,
+                                errors.NumbaWarning,
+                                self.func_id.filename,
+                                self.func_id.firstlineno)
+
+
     def stage_inline_pass(self):
         """
         Inline calls to locally defined closures.


Test patch:
diff --git a/numba/tests/test_debug.py b/numba/tests/test_debug.py
--- a/numba/tests/test_debug.py
+++ b/numba/tests/test_debug.py
@@ -3,12 +3,16 @@
 import os
 import platform
 import textwrap
+import warnings
+
+import numpy as np
 
 from .support import TestCase, override_config, captured_stdout, forbid_codegen
 from numba import unittest_support as unittest
 from numba import jit, jitclass, types
-from numba.compiler import compile_isolated
+from numba.compiler import compile_isolated, Flags
 from numba import compiler
+from .test_parfors import skip_unsupported
 
 def simple_nopython(somearg):
     retval = somearg + 1
@@ -28,6 +32,12 @@ def __init__(self):
 def simple_class_user(obj):
     return obj.h
 
+def unsupported_parfor(a, b):
+    return np.dot(a, b) # dot as gemm unsupported
+
+force_parallel_flags = Flags()
+force_parallel_flags.set("auto_parallel")
+force_parallel_flags.set('nrt')
 
 class DebugTestBase(TestCase):
 
@@ -199,6 +209,36 @@ def test_debug(self):
         out = self.compile_simple_nopython()
         self.assertFalse(out)
 
+class TestParforWarnings(TestCase):
+    """
+    Test that using parallel=True on a function that does not have parallel
+    semantics warns if NUMBA_WARNINGS is set.
+    """
+
+    def check_parfors_warning(self, warn_list):
+        msg = ("parallel=True was specified but no transformation for parallel"
+               " execution was possible.")
+        warning_found = False
+        for w in warn_list:
+            if msg in str(w.message):
+                warning_found = True
+                break
+        self.assertTrue(warning_found, "Warning message should be found.")
+
+    @skip_unsupported
+    def test_warns(self):
+        try:
+            arr_ty = types.Array(types.float64, 2, "C")
+            os.environ['NUMBA_WARNINGS'] = '1'
+            with warnings.catch_warnings(record=True) as w:
+                warnings.simplefilter("always")
+                cres = compile_isolated(unsupported_parfor, (arr_ty, arr_ty),
+                                        flags=force_parallel_flags)
+
+            self.check_parfors_warning(w)
+        finally:
+            del os.environ['NUMBA_WARNINGS']
+
 
 if __name__ == '__main__':
     unittest.main()