File: C:/Users/fred/anaconda3/Lib/site-packages/numba/cuda/tests/cudadrv/test_emm_plugins.py
import ctypes
import numpy as np
import weakref
from numba import cuda
from numba.core import config
from numba.cuda.testing import unittest, CUDATestCase, skip_on_cudasim
from numba.tests.support import linux_only
if not config.ENABLE_CUDASIM:
class DeviceOnlyEMMPlugin(cuda.HostOnlyCUDAMemoryManager):
"""
Dummy EMM Plugin implementation for testing. It memorises which plugin
API methods have been called so that the tests can check that Numba
called into the plugin as expected.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# For tracking our dummy allocations
self.allocations = {}
self.count = 0
# For tracking which methods have been called
self.initialized = False
self.memalloc_called = False
self.reset_called = False
self.get_memory_info_called = False
self.get_ipc_handle_called = False
def memalloc(self, size):
# We maintain a list of allocations and keep track of them, so that
# we can test that the finalizers of objects returned by memalloc
# get called.
# Numba should have initialized the memory manager when preparing
# the context for use, prior to any memalloc call.
if not self.initialized:
raise RuntimeError("memalloc called before initialize")
self.memalloc_called = True
# Create an allocation and record it
self.count += 1
alloc_count = self.count
self.allocations[alloc_count] = size
# The finalizer deletes the record from our internal dict of
# allocations.
finalizer_allocs = self.allocations
def finalizer():
del finalizer_allocs[alloc_count]
# We use an AutoFreePointer so that the finalizer will be run when
# the reference count drops to zero.
ctx = weakref.proxy(self.context)
ptr = ctypes.c_void_p(alloc_count)
return cuda.cudadrv.driver.AutoFreePointer(ctx, ptr, size,
finalizer=finalizer)
def initialize(self):
# No special initialization needed.
self.initialized = True
def reset(self):
# We remove all allocations on reset, just as a real EMM Plugin
# would do. Note that our finalizers in memalloc don't check
# whether the allocations are still alive, so running them after
# reset will detect any allocations that are floating around at
# exit time; however, the atexit finalizer for weakref will only
# print a traceback, not terminate the interpreter abnormally.
self.reset_called = True
def get_memory_info(self):
# Return some dummy memory information
self.get_memory_info_called = True
return cuda.MemoryInfo(free=32, total=64)
def get_ipc_handle(self, memory):
# The dummy IPC handle is only a string, so it is important that
# the tests don't try to do too much with it (e.g. open / close
# it).
self.get_ipc_handle_called = True
return "Dummy IPC handle for alloc %s" % memory.device_pointer.value
@property
def interface_version(self):
# The expected version for an EMM Plugin.
return 1
class BadVersionEMMPlugin(DeviceOnlyEMMPlugin):
"""A plugin that claims to implement a different interface version"""
@property
def interface_version(self):
return 2
@skip_on_cudasim('EMM Plugins not supported on CUDA simulator')
class TestDeviceOnlyEMMPlugin(CUDATestCase):
"""
Tests that the API of an EMM Plugin that implements device allocations
only is used correctly by Numba.
"""
def setUp(self):
super().setUp()
# Always start afresh with a new context and memory manager
cuda.close()
cuda.set_memory_manager(DeviceOnlyEMMPlugin)
def tearDown(self):
super().tearDown()
# Unset the memory manager for subsequent tests
cuda.close()
cuda.cudadrv.driver._memory_manager = None
def test_memalloc(self):
mgr = cuda.current_context().memory_manager
# Allocate an array and check that memalloc was called with the correct
# size.
arr_1 = np.arange(10)
d_arr_1 = cuda.device_array_like(arr_1)
self.assertTrue(mgr.memalloc_called)
self.assertEqual(mgr.count, 1)
self.assertEqual(mgr.allocations[1], arr_1.nbytes)
# Allocate again, with a different size, and check that it is also
# correct.
arr_2 = np.arange(5)
d_arr_2 = cuda.device_array_like(arr_2)
self.assertEqual(mgr.count, 2)
self.assertEqual(mgr.allocations[2], arr_2.nbytes)
# Remove the first array, and check that our finalizer was called for
# the first array only.
del d_arr_1
self.assertNotIn(1, mgr.allocations)
self.assertIn(2, mgr.allocations)
# Remove the second array and check that its finalizer was also
# called.
del d_arr_2
self.assertNotIn(2, mgr.allocations)
def test_initialized_in_context(self):
# If we have a CUDA context, it should already have initialized its
# memory manager.
self.assertTrue(cuda.current_context().memory_manager.initialized)
def test_reset(self):
ctx = cuda.current_context()
ctx.reset()
self.assertTrue(ctx.memory_manager.reset_called)
def test_get_memory_info(self):
ctx = cuda.current_context()
meminfo = ctx.get_memory_info()
self.assertTrue(ctx.memory_manager.get_memory_info_called)
self.assertEqual(meminfo.free, 32)
self.assertEqual(meminfo.total, 64)
@linux_only
def test_get_ipc_handle(self):
# We don't attempt to close the IPC handle in this test because Numba
# will be expecting a real IpcHandle object to have been returned from
# get_ipc_handle, and it would cause problems to do so.
arr = np.arange(2)
d_arr = cuda.device_array_like(arr)
ipch = d_arr.get_ipc_handle()
ctx = cuda.current_context()
self.assertTrue(ctx.memory_manager.get_ipc_handle_called)
self.assertIn("Dummy IPC handle for alloc 1", ipch._ipc_handle)
@skip_on_cudasim('EMM Plugins not supported on CUDA simulator')
class TestBadEMMPluginVersion(CUDATestCase):
"""
Ensure that Numba rejects EMM Plugins with incompatible version
numbers.
"""
def test_bad_plugin_version(self):
with self.assertRaises(RuntimeError) as raises:
cuda.set_memory_manager(BadVersionEMMPlugin)
self.assertIn('version 1 required', str(raises.exception))
if __name__ == '__main__':
unittest.main()