Repository: numba/numba

Issue:
CUDA Fails to copy_to_host Array Slice
I'm trying to copy part of a large device array to the host. A minimal repro, based on [this](https://github.com/numba/numba/blob/1762237f4b3685955e6ca9bdc4d40fe21fd10e9b/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py#L73-L83) unit test in numba (the original test works):

````python
arr = np.arange(3 ** 3).reshape(3, 3, 3)
darr = cuda.to_device(arr)
expect = arr[2]
sliced = darr[2]
assert expect.shape == sliced.shape
assert expect.strides == sliced.strides
got = sliced.copy_to_host()
assert np.all(expect == got)
````

Gives:

````
---------------------------------------------------------------------------
CudaAPIError                              Traceback (most recent call last)
<ipython-input-9-f28b90ee49ff> in <module>()
      7 assert expect.shape == sliced.shape
      8 assert expect.strides == sliced.strides
----> 9 got = sliced.copy_to_host()
     10 assert np.all(expect == got)

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\devices.py in _require_cuda_context(*args, **kws)
    210     def _require_cuda_context(*args, **kws):
    211         get_context()
--> 212         return fn(*args, **kws)
    213 
    214     return _require_cuda_context

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\devicearray.py in copy_to_host(self, ary, stream)
    200         assert self.alloc_size >= 0, "Negative memory size"
    201         if self.alloc_size != 0:
--> 202             _driver.device_to_host(hostary, self, self.alloc_size, stream=stream)
    203 
    204         if ary is None:

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in device_to_host(dst, src, size, stream)
   1604         fn = driver.cuMemcpyDtoH
   1605 
-> 1606     fn(host_pointer(dst), device_pointer(src), size, *varargs)
   1607 
   1608 

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in safe_cuda_api_call(*args)
    286             _logger.debug('call driver api: %s', libfn.__name__)
    287             retcode = libfn(*args)
--> 288             self._check_error(fname, retcode)
    289         return safe_cuda_api_call
    290 

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in _check_error(self, fname, retcode)
    321                     _logger.critical(msg, _getpid(), self.pid)
    322                     raise CudaDriverError("CUDA initialized before forking")
--> 323             raise CudaAPIError(retcode, msg)
    324 
    325     def get_device(self, devnum=0):

CudaAPIError: [1] Call to cuMemcpyDtoH results in CUDA_ERROR_INVALID_VALUE
````

I'm using the latest numba:
````python
from numba import _version, cuda
_version.get_versions()
# {'full': '540650dbcffcdc94424596987fc63a49352525ef', 'version': '0.36.2'}
````
CUDA Fails to copy_to_host Array Slice
I'm trying to copy part of a large device array to the host. A minimal repro, based on [this](https://github.com/numba/numba/blob/1762237f4b3685955e6ca9bdc4d40fe21fd10e9b/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py#L73-L83) unit test in numba (the original test works):

````python
arr = np.arange(3 ** 3).reshape(3, 3, 3)
darr = cuda.to_device(arr)
expect = arr[2]
sliced = darr[2]
assert expect.shape == sliced.shape
assert expect.strides == sliced.strides
got = sliced.copy_to_host()
assert np.all(expect == got)
````

Gives:

````
---------------------------------------------------------------------------
CudaAPIError                              Traceback (most recent call last)
<ipython-input-9-f28b90ee49ff> in <module>()
      7 assert expect.shape == sliced.shape
      8 assert expect.strides == sliced.strides
----> 9 got = sliced.copy_to_host()
     10 assert np.all(expect == got)

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\devices.py in _require_cuda_context(*args, **kws)
    210     def _require_cuda_context(*args, **kws):
    211         get_context()
--> 212         return fn(*args, **kws)
    213 
    214     return _require_cuda_context

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\devicearray.py in copy_to_host(self, ary, stream)
    200         assert self.alloc_size >= 0, "Negative memory size"
    201         if self.alloc_size != 0:
--> 202             _driver.device_to_host(hostary, self, self.alloc_size, stream=stream)
    203 
    204         if ary is None:

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in device_to_host(dst, src, size, stream)
   1604         fn = driver.cuMemcpyDtoH
   1605 
-> 1606     fn(host_pointer(dst), device_pointer(src), size, *varargs)
   1607 
   1608 

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in safe_cuda_api_call(*args)
    286             _logger.debug('call driver api: %s', libfn.__name__)
    287             retcode = libfn(*args)
--> 288             self._check_error(fname, retcode)
    289         return safe_cuda_api_call
    290 

c:\users\nwhite\appdata\local\continuum\anaconda3\lib\site-packages\numba\cuda\cudadrv\driver.py in _check_error(self, fname, retcode)
    321                     _logger.critical(msg, _getpid(), self.pid)
    322                     raise CudaDriverError("CUDA initialized before forking")
--> 323             raise CudaAPIError(retcode, msg)
    324 
    325     def get_device(self, devnum=0):

CudaAPIError: [1] Call to cuMemcpyDtoH results in CUDA_ERROR_INVALID_VALUE
````

I'm using the latest numba:
````python
from numba import _version, cuda
_version.get_versions()
# {'full': '540650dbcffcdc94424596987fc63a49352525ef', 'version': '0.36.2'}
````


Discussion:
I can confirm that it's a bug.  Interestingly, `darr[2:][0]` works but `darr[2]` doesn't.
I can confirm that it's a bug.  Interestingly, `darr[2:][0]` works but `darr[2]` doesn't.

Resolving patch:
diff --git a/numba/dummyarray.py b/numba/dummyarray.py
--- a/numba/dummyarray.py
+++ b/numba/dummyarray.py
@@ -163,6 +163,7 @@ def from_desc(cls, offset, shape, strides, itemsize):
             dim = Dim(offset, offset + ashape * astride, ashape, astride,
                       single=False)
             dims.append(dim)
+            offset = 0  # offset only applies to first dimension
         return cls(dims, itemsize)
 
     def __init__(self, dims, itemsize):


Test patch:
diff --git a/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py b/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py
--- a/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py
+++ b/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py
@@ -82,6 +82,27 @@ def test_prefix_2d(self):
                 got = sliced.copy_to_host()
                 self.assertTrue(np.all(expect == got))
 
+    def test_select_3d_first_two_dim(self):
+        arr = np.arange(3 ** 3).reshape(3, 3, 3)
+        darr = cuda.to_device(arr)
+        # Select first dimension
+        for i in range(arr.shape[0]):
+            expect = arr[i]
+            sliced = darr[i]
+            self.assertEqual(expect.shape, sliced.shape)
+            self.assertEqual(expect.strides, sliced.strides)
+            got = sliced.copy_to_host()
+            self.assertTrue(np.all(expect == got))
+        # Select second dimension
+        for i in range(arr.shape[0]):
+            for j in range(arr.shape[1]):
+                expect = arr[i, j]
+                sliced = darr[i, j]
+                self.assertEqual(expect.shape, sliced.shape)
+                self.assertEqual(expect.strides, sliced.strides)
+                got = sliced.copy_to_host()
+                self.assertTrue(np.all(expect == got))
+
     def test_select_f(self):
         a = np.arange(5 * 5 * 5).reshape(5, 5, 5, order='F')
         da = cuda.to_device(a)