1313
1414try :
1515 from datashader .transfer_functions ._cuda_utils import (
16- cuda_atomic_nanmin , cuda_atomic_nanmax , cuda_args )
16+ cuda_atomic_nanmin , cuda_atomic_nanmax , cuda_args , cuda_mutex_lock , cuda_mutex_unlock ,
17+ cuda_nanmax_n_in_place , cuda_nanmin_n_in_place )
1718except ImportError :
1819 cuda_atomic_nanmin , cuda_atomic_nanmmax , cuda_args = None , None , None
1920
@@ -245,6 +246,9 @@ class Reduction(Expr):
245246 def __init__ (self , column = None ):
246247 self .column = column
247248
249+ def uses_cuda_mutex (self ):
250+ return False
251+
248252 def uses_row_index (self ):
249253 return False
250254
@@ -1324,6 +1328,9 @@ def _combine(aggs):
13241328
13251329
13261330class max_n (FloatingNReduction ):
1331+ def uses_cuda_mutex (self ):
1332+ return True
1333+
13271334 def _antialias_stage_2 (self , self_intersect , array_module ):
13281335 return ((AntialiasCombination .MAX , array_module .nan ),)
13291336
@@ -1344,10 +1351,33 @@ def _append(x, y, agg, field):
13441351 return i
13451352 return - 1
13461353
1347- def _build_append (self , dshape , schema , cuda , antialias , self_intersect ):
1354+ # GPU append functions
1355+ @staticmethod
1356+ @nb_cuda .jit (device = True )
1357+ def _append_cuda (x , y , agg , field , mutex ):
1358+ if not isnull (field ):
1359+ # Linear walk along stored values.
1360+ # Could do binary search instead but not expecting n to be large.
1361+ n = agg .shape [2 ]
1362+ index = (x , y )
1363+ cuda_mutex_lock (mutex , index )
1364+ for i in range (n ):
1365+ if isnull (agg [y , x , i ]) or field > agg [y , x , i ]:
1366+ # Bump previous values along to make room for new value.
1367+ for j in range (n - 1 , i , - 1 ):
1368+ agg [y , x , j ] = agg [y , x , j - 1 ]
1369+ agg [y , x , i ] = field
1370+
1371+ cuda_mutex_unlock (mutex , index )
1372+ return i
1373+ cuda_mutex_unlock (mutex , index )
1374+ return - 1
1375+
1376+ def _build_combine (self , dshape , antialias , cuda ):
13481377 if cuda :
1349- raise ValueError ("'max_n' reduction is not supported on the GPU" )
1350- return super ()._build_append (dshape , schema , cuda , antialias , self_intersect )
1378+ return self ._combine_cuda
1379+ else :
1380+ return self ._combine
13511381
13521382 @staticmethod
13531383 def _combine (aggs ):
@@ -1356,8 +1386,19 @@ def _combine(aggs):
13561386 nanmax_n_in_place (ret , aggs [i ])
13571387 return ret
13581388
1389+ @staticmethod
1390+ def _combine_cuda (aggs ):
1391+ ret = aggs [0 ]
1392+ kernel_args = cuda_args (ret .shape [:2 ])
1393+ for i in range (1 , len (aggs )):
1394+ cuda_nanmax_n_in_place [kernel_args ](ret , aggs [i ])
1395+ return ret
1396+
13591397
13601398class min_n (FloatingNReduction ):
1399+ def uses_cuda_mutex (self ):
1400+ return True
1401+
13611402 def _antialias_requires_2_stages (self ):
13621403 return True
13631404
@@ -1381,10 +1422,33 @@ def _append(x, y, agg, field):
13811422 return i
13821423 return - 1
13831424
1384- def _build_append (self , dshape , schema , cuda , antialias , self_intersect ):
1425+ # GPU append functions
1426+ @staticmethod
1427+ @nb_cuda .jit (device = True )
1428+ def _append_cuda (x , y , agg , field , mutex ):
1429+ if not isnull (field ):
1430+ # Linear walk along stored values.
1431+ # Could do binary search instead but not expecting n to be large.
1432+ n = agg .shape [2 ]
1433+ index = (x , y )
1434+ cuda_mutex_lock (mutex , index )
1435+ for i in range (n ):
1436+ if isnull (agg [y , x , i ]) or field < agg [y , x , i ]:
1437+ # Bump previous values along to make room for new value.
1438+ for j in range (n - 1 , i , - 1 ):
1439+ agg [y , x , j ] = agg [y , x , j - 1 ]
1440+ agg [y , x , i ] = field
1441+
1442+ cuda_mutex_unlock (mutex , index )
1443+ return i
1444+ cuda_mutex_unlock (mutex , index )
1445+ return - 1
1446+
1447+ def _build_combine (self , dshape , antialias , cuda ):
13851448 if cuda :
1386- raise ValueError ("'min_n' reduction is not supported on the GPU" )
1387- return super ()._build_append (dshape , schema , cuda , antialias , self_intersect )
1449+ return self ._combine_cuda
1450+ else :
1451+ return self ._combine
13881452
13891453 @staticmethod
13901454 def _combine (aggs ):
@@ -1393,6 +1457,14 @@ def _combine(aggs):
13931457 nanmin_n_in_place (ret , aggs [i ])
13941458 return ret
13951459
1460+ @staticmethod
1461+ def _combine_cuda (aggs ):
1462+ ret = aggs [0 ]
1463+ kernel_args = cuda_args (ret .shape [:2 ])
1464+ for i in range (1 , len (aggs )):
1465+ cuda_nanmin_n_in_place [kernel_args ](ret , aggs [i ])
1466+ return ret
1467+
13961468
13971469class mode (Reduction ):
13981470 """Mode (most common value) of all the values encountered in ``column``.
@@ -1472,6 +1544,9 @@ def out_dshape(self, input_dshape, antialias):
14721544 else :
14731545 return dshape (ct .float64 )
14741546
1547+ def uses_cuda_mutex (self ):
1548+ return self .selector .uses_cuda_mutex ()
1549+
14751550 def uses_row_index (self ):
14761551 return self .column is None
14771552
@@ -1543,6 +1618,10 @@ def _build_bases(self, cuda=False):
15431618 return self .selector ._build_bases (cuda = cuda ) + super ()._build_bases (cuda = cuda )
15441619
15451620 def _build_combine (self , dshape , antialias , cuda ):
1621+ if cuda and self .uses_cuda_mutex ():
1622+ raise NotImplementedError (
1623+ "'where' reduction does not support a selector that uses a CUDA mutex such as 'max_n'" )
1624+
15461625 # Does not support categorical reductions.
15471626 selector = self .selector
15481627 append = selector ._append
0 commit comments