Skip to content

Commit aec1143

Browse files
committed
Merge pull request numba#9005 from gmarkall/issue-8923
Fix: Issue numba#8923 - avoid spurious device-to-host transfers in CUDA ufuncs
1 parent 1cd5301 commit aec1143

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

‎numba/cuda/tests/cudapy/test_vectorize.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from itertools import product
55
from numba import vectorize
66
from numba import cuda, int32, float32, float64
7+
from numba.cuda.cudadrv.driver import CudaAPIError, driver
78
from numba.cuda.testing import skip_on_cudasim
89
from numba.cuda.testing import CUDATestCase
910
import unittest
@@ -227,6 +228,56 @@ def bar(x):
227228

228229
self.assertEqual(bar.__name__, 'bar')
229230

231+
def test_no_transfer_for_device_data(self):
232+
# Initialize test data on the device prior to banning host <-> device
233+
# transfer
234+
235+
noise = np.random.randn(1, 3, 64, 64).astype(np.float32)
236+
noise = cuda.to_device(noise)
237+
238+
# A mock of a CUDA function that always raises a CudaAPIError
239+
240+
def raising_transfer(*args, **kwargs):
241+
raise CudaAPIError(999, 'Transfer not allowed')
242+
243+
# Use the mock for transfers between the host and device
244+
245+
old_HtoD = getattr(driver, 'cuMemcpyHtoD', None)
246+
old_DtoH = getattr(driver, 'cuMemcpyDtoH', None)
247+
248+
setattr(driver, 'cuMemcpyHtoD', raising_transfer)
249+
setattr(driver, 'cuMemcpyDtoH', raising_transfer)
250+
251+
# Ensure that the mock functions are working as expected
252+
253+
with self.assertRaisesRegex(CudaAPIError, "Transfer not allowed"):
254+
noise.copy_to_host()
255+
256+
with self.assertRaisesRegex(CudaAPIError, "Transfer not allowed"):
257+
cuda.to_device([1])
258+
259+
try:
260+
# Check that defining and calling a ufunc with data on the device
261+
# induces no transfers
262+
263+
@vectorize(['float32(float32)'], target='cuda')
264+
def func(noise):
265+
return noise + 1.0
266+
267+
func(noise)
268+
finally:
269+
# Replace our mocks with the original implementations. If there was
270+
# no original implementation, simply remove ours.
271+
272+
if old_HtoD is not None:
273+
setattr(driver, 'cuMemcpyHtoD', old_HtoD)
274+
else:
275+
del driver.cuMemcpyHtoD
276+
if old_DtoH is not None:
277+
setattr(driver, 'cuMemcpyDtoH', old_DtoH)
278+
else:
279+
del driver.cuMemcpyDtoH
280+
230281

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

‎numba/np/ufunc/deviceufunc.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ def _fill_argtypes(self):
105105
"""
106106
for i, ary in enumerate(self.arrays):
107107
if ary is not None:
108-
self.argtypes[i] = np.asarray(ary).dtype
108+
dtype = getattr(ary, 'dtype')
109+
if dtype is None:
110+
dtype = np.asarray(ary).dtype
111+
self.argtypes[i] = dtype
109112

110113
def _resolve_signature(self):
111114
"""Resolve signature.

0 commit comments

Comments
 (0)