2121from .driver import driver
2222
2323
24- class _DeviceList (object ):
25- def __getattr__ (self , attr ):
26- # First time looking at "lst" attribute.
27- if attr == "lst" :
28- # Device list is not initialized.
29- # Query all CUDA devices.
30- numdev = driver .get_device_count ()
31- gpus = [
32- _DeviceContextManager (driver .get_device (devid ))
33- for devid in range (numdev )
34- ]
35- # Define "lst" to avoid re-initialization
36- self .lst = gpus
37- return gpus
38-
39- # Other attributes
40- return super (_DeviceList , self ).__getattr__ (attr )
24+ class _DeviceList :
25+ @property
26+ @functools .cache
27+ def lst (self ):
28+ return [
29+ _DeviceContextManager (driver .get_device (devid ))
30+ for devid in range (driver .get_device_count ())
31+ ]
4132
4233 def __getitem__ (self , devnum ):
4334 """
@@ -79,6 +70,9 @@ class _DeviceContextManager(object):
7970
8071 def __init__ (self , device ):
8172 self ._device = device
73+ # Forwarded directly, to avoid the performance overhead of
74+ # `__getattr__` and method lookup for a commonly accessed method
75+ self .get_primary_context = self ._device .get_primary_context
8276
8377 def __getattr__ (self , item ):
8478 return getattr (self ._device , item )
@@ -88,10 +82,10 @@ def __enter__(self):
8882
8983 def __exit__ (self , exc_type , exc_val , exc_tb ):
9084 # this will verify that we are popping the right device context.
91- self ._device . get_primary_context ().pop ()
85+ self .get_primary_context ().pop ()
9286
9387 def __str__ (self ):
94- return "<Managed Device {self.id}>" . format ( self = self )
88+ return f "<Managed Device { self .id } >"
9589
9690
9791class _Runtime (object ):
@@ -147,7 +141,8 @@ def get_or_create_context(self, devnum):
147141 return attached_ctx
148142 else :
149143 devnum = int (devnum )
150- return self ._activate_context_for (devnum )
144+ with self ._lock :
145+ return self ._activate_context_for (devnum )
151146
152147 def _get_or_create_context_uncached (self , devnum ):
153148 """See also ``get_or_create_context(devnum)``.
@@ -166,28 +161,29 @@ def _get_or_create_context_uncached(self, devnum):
166161 ctx_handle = ctx .handle .value
167162 ac_ctx_handle = ac .context_handle .value
168163 if ctx_handle != ac_ctx_handle :
169- msg = (
164+ raise RuntimeError (
170165 "Numba cannot operate on non-primary"
171- " CUDA context {:x}"
166+ f " CUDA context { ac_ctx_handle :x} "
172167 )
173- raise RuntimeError (msg .format (ac_ctx_handle ))
174168 # Ensure the context is ready
175169 ctx .prepare_for_use ()
176170 return ctx
177171
178172 def _activate_context_for (self , devnum ):
179- with self ._lock :
180- gpu = self .gpus [devnum ]
181- newctx = gpu .get_primary_context ()
182- # Detect unexpected context switch
183- cached_ctx = self ._get_attached_context ()
184- if cached_ctx is not None and cached_ctx is not newctx :
185- raise RuntimeError ("Cannot switch CUDA-context." )
186- newctx .push ()
187- return newctx
173+ gpu = self .gpus [devnum ]
174+ newctx = gpu .get_primary_context ()
175+ # Detect unexpected context switch
176+ cached_ctx = self ._get_attached_context ()
177+ if cached_ctx is not None and cached_ctx is not newctx :
178+ raise RuntimeError ("Cannot switch CUDA-context." )
179+ newctx .push ()
180+ return newctx
188181
189182 def _get_attached_context (self ):
190- return getattr (self ._tls , "attached_context" , None )
183+ try :
184+ return self ._tls .attached_context
185+ except AttributeError :
186+ return None
191187
192188 def _set_attached_context (self , ctx ):
193189 self ._tls .attached_context = ctx
0 commit comments