test_exception.py 5.13 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import numpy as np

from numba import cuda
from numba.cuda.testing import unittest, xfail_unless_cudasim, CUDATestCase
from numba.core import config


class TestException(CUDATestCase):
    def test_exception(self):
        def foo(ary):
            x = cuda.threadIdx.x
            if x == 2:
                # NOTE: indexing with a out-of-bounds constant can fail at
                # compile-time instead (because the getitem is rewritten as a
                # static_getitem)
                ary.shape[-x]

        unsafe_foo = cuda.jit(foo)
        safe_foo = cuda.jit(debug=True, opt=False)(foo)

        if not config.ENABLE_CUDASIM:
            # Simulator throws exceptions regardless of debug
            # setting
            unsafe_foo[1, 3](np.array([0, 1]))

        with self.assertRaises(IndexError) as cm:
            safe_foo[1, 3](np.array([0, 1]))
        self.assertIn("tuple index out of range", str(cm.exception))

    def test_user_raise(self):
        @cuda.jit(debug=True, opt=False)
        def foo(do_raise):
            if do_raise:
                raise ValueError

        foo[1, 1](False)
        with self.assertRaises(ValueError):
            foo[1, 1](True)

    def case_raise_causing_warp_diverge(self, with_debug_mode):
        """Testing issue #2655.

        Exception raising code can cause the compiler to miss location
        of unifying branch target and resulting in unexpected warp
        divergence.
        """
        with_opt_mode = not with_debug_mode

        @cuda.jit(debug=with_debug_mode, opt=with_opt_mode)
        def problematic(x, y):
            tid = cuda.threadIdx.x
            ntid = cuda.blockDim.x

            if tid > 12:
                for i in range(ntid):
                    y[i] += x[i] // y[i]

            cuda.syncthreads()
            if tid < 17:
                for i in range(ntid):
                    x[i] += x[i] // y[i]

        @cuda.jit
        def oracle(x, y):
            tid = cuda.threadIdx.x
            ntid = cuda.blockDim.x

            if tid > 12:
                for i in range(ntid):
                    if y[i] != 0:
                        y[i] += x[i] // y[i]

            cuda.syncthreads()
            if tid < 17:
                for i in range(ntid):
                    if y[i] != 0:
                        x[i] += x[i] // y[i]

        n = 32
        got_x = 1. / (np.arange(n) + 0.01)
        got_y = 1. / (np.arange(n) + 0.01)
        problematic[1, n](got_x, got_y)

        expect_x = 1. / (np.arange(n) + 0.01)
        expect_y = 1. / (np.arange(n) + 0.01)
        oracle[1, n](expect_x, expect_y)

        np.testing.assert_almost_equal(expect_x, got_x)
        np.testing.assert_almost_equal(expect_y, got_y)

    def test_raise_causing_warp_diverge(self):
        """Test case for issue #2655.
        """
        self.case_raise_causing_warp_diverge(with_debug_mode=False)

    # The following two cases relate to Issue #7806: Division by zero stops the
    # kernel. https://github.com/numba/numba/issues/7806.

    def test_no_zero_division_error(self):
        # When debug is False:
        # - Division by zero raises no exception
        # - Execution proceeds after a divide by zero
        @cuda.jit
        def f(r, x, y):
            r[0] = y[0] / x[0]
            r[1] = y[0]

        r = np.zeros(2)
        x = np.zeros(1)
        y = np.ones(1)

        f[1, 1](r, x, y)

        self.assertTrue(np.isinf(r[0]), 'Expected inf from div by zero')
        self.assertEqual(r[1], y[0], 'Expected execution to continue')

    def test_zero_division_error_in_debug(self):
        # When debug is True:
        # - Zero by division raises an exception
        # - Execution halts at the point of division by zero
        @cuda.jit(debug=True, opt=False)
        def f(r, x, y):
            r[0] = y[0] / x[0]
            r[1] = y[0]

        r = np.zeros(2)
        x = np.zeros(1)
        y = np.ones(1)

        # Simulator and device behaviour differs slightly in the exception
        # raised - in debug mode, the CUDA target uses the Python error model,
        # which gives a ZeroDivision error. The simulator uses NumPy with the
        # error mode for division by zero set to raise, which results in a
        # FloatingPointError instead.
        if config.ENABLE_CUDASIM:
            exc = FloatingPointError
        else:
            exc = ZeroDivisionError

        with self.assertRaises(exc):
            f[1, 1](r, x, y)

        self.assertEqual(r[0], 0, 'Expected result to be left unset')
        self.assertEqual(r[1], 0, 'Expected execution to stop')

    @xfail_unless_cudasim
    def test_raise_in_device_function(self):
        # This is an expected failure because reporting of exceptions raised in
        # device functions does not work correctly - see Issue #8036:
        # https://github.com/numba/numba/issues/8036
        msg = 'Device Function Error'

        @cuda.jit(device=True)
        def f():
            raise ValueError(msg)

        @cuda.jit(debug=True)
        def kernel():
            f()

        with self.assertRaises(ValueError) as raises:
            kernel[1, 1]()

        self.assertIn(msg, str(raises.exception))


if __name__ == '__main__':
    unittest.main()