@@ -48,12 +48,20 @@ def __sycl_usm_array_interface(self):
4848 not has_sycl_platforms (),
4949 reason = "No SYCL devices except the default host device." ,
5050)
51- def test_memory_create ():
51+ def test_memory_create (memory_ctor ):
52+ import sys
53+
5254 nbytes = 1024
53- queue = dpctl .get_current_queue ()
54- mobj = MemoryUSMShared (nbytes , alignment = 64 , queue = queue )
55+ queue = dpctl .SyclQueue ()
56+ mobj = memory_ctor (nbytes , alignment = 64 , queue = queue )
5557 assert mobj .nbytes == nbytes
5658 assert hasattr (mobj , "__sycl_usm_array_interface__" )
59+ assert len (mobj ) == nbytes
60+ assert mobj .size == nbytes
61+ assert mobj ._context == queue .sycl_context
62+ assert type (repr (mobj )) is str
63+ assert type (bytes (mobj )) is bytes
64+ assert sys .getsizeof (mobj ) > nbytes
5765
5866
5967@pytest .mark .skipif (
@@ -69,7 +77,7 @@ def test_memory_create_with_np():
6977
7078def _create_memory ():
7179 nbytes = 1024
72- queue = dpctl .get_current_queue ()
80+ queue = dpctl .SyclQueue ()
7381 mobj = MemoryUSMShared (nbytes , alignment = 64 , queue = queue )
7482 return mobj
7583
@@ -90,38 +98,36 @@ def test_memory_without_context():
9098
9199 # Without context
92100 assert mobj .get_usm_type () == "shared"
101+ assert mobj .get_usm_type (syclobj = dpctl .SyclContext ()) == "shared"
93102
94103
95104@pytest .mark .skipif (not has_cpu (), reason = "No SYCL CPU device available." )
96105def test_memory_cpu_context ():
97106 mobj = _create_memory ()
98107
99- # CPU context
100- with dpctl .device_context ("opencl:cpu:0" ):
101- # type respective to the context in which
102- # memory was created
103- usm_type = mobj .get_usm_type ()
104- assert usm_type == "shared"
108+ # type respective to the context in which
109+ # memory was created
110+ usm_type = mobj .get_usm_type ()
111+ assert usm_type == "shared"
105112
106- current_queue = dpctl .get_current_queue ( )
107- # type as view from current queue
108- usm_type = mobj .get_usm_type (current_queue )
109- # type can be unknown if current queue is
110- # not in the same SYCL context
111- assert usm_type in ["unknown" , "shared" ]
113+ cpu_queue = dpctl .SyclQueue ( "cpu" )
114+ # type as view from CPU queue
115+ usm_type = mobj .get_usm_type (cpu_queue )
116+ # type can be unknown if current queue is
117+ # not in the same SYCL context
118+ assert usm_type in ["unknown" , "shared" ]
112119
113120
114121@pytest .mark .skipif (not has_gpu (), reason = "No OpenCL GPU queues available" )
115122def test_memory_gpu_context ():
116123 mobj = _create_memory ()
117124
118125 # GPU context
119- with dpctl .device_context ("opencl:gpu:0" ):
120- usm_type = mobj .get_usm_type ()
121- assert usm_type == "shared"
122- current_queue = dpctl .get_current_queue ()
123- usm_type = mobj .get_usm_type (current_queue )
124- assert usm_type in ["unknown" , "shared" ]
126+ usm_type = mobj .get_usm_type ()
127+ assert usm_type == "shared"
128+ gpu_queue = dpctl .SyclQueue ("opencl:gpu" )
129+ usm_type = mobj .get_usm_type (gpu_queue )
130+ assert usm_type in ["unknown" , "shared" ]
125131
126132
127133@pytest .mark .skipif (
@@ -166,10 +172,10 @@ def test_zero_copy():
166172 not has_sycl_platforms (),
167173 reason = "No SYCL devices except the default host device." ,
168174)
169- def test_pickling ():
175+ def test_pickling (memory_ctor ):
170176 import pickle
171177
172- mobj = _create_memory ( )
178+ mobj = memory_ctor ( 1024 , alignment = 64 )
173179 host_src_obj = _create_host_buf (mobj .nbytes )
174180 mobj .copy_from_host (host_src_obj )
175181
@@ -185,6 +191,22 @@ def test_pickling():
185191 ), "Pickling/unpickling should be changing pointer"
186192
187193
194+ @pytest .mark .skipif (
195+ not has_sycl_platforms (),
196+ reason = "No SYCL devices except the default host device." ,
197+ )
198+ def test_pickling_reconstructor_invalid_type (memory_ctor ):
199+ import pickle
200+
201+ mobj = memory_ctor (1024 , alignment = 64 )
202+ good_pickle_bytes = pickle .dumps (mobj )
203+ usm_types = expected_usm_type (memory_ctor ).encode ("utf-8" )
204+ i = good_pickle_bytes .index (usm_types )
205+ bad_pickle_bytes = good_pickle_bytes [:i ] + b"u" + good_pickle_bytes [i + 1 :]
206+ with pytest .raises (ValueError ):
207+ pickle .loads (bad_pickle_bytes )
208+
209+
188210@pytest .fixture (params = [MemoryUSMShared , MemoryUSMDevice , MemoryUSMHost ])
189211def memory_ctor (request ):
190212 return request .param
@@ -256,12 +278,15 @@ def test_sycl_usm_array_interface(memory_ctor):
256278
257279
258280class View :
259- def __init__ (self , buf , shape , strides , offset , syclobj = None ):
281+ def __init__ (
282+ self , buf , shape , strides , offset , syclobj = None , transf_fn = None
283+ ):
260284 self .buffer_ = buf
261285 self .shape_ = shape
262286 self .strides_ = strides
263287 self .offset_ = offset
264288 self .syclobj_ = syclobj
289+ self .transf_fn_ = transf_fn
265290
266291 @property
267292 def __sycl_usm_array_interface__ (self ):
@@ -271,6 +296,8 @@ def __sycl_usm_array_interface__(self):
271296 sua_iface ["strides" ] = self .strides_
272297 if self .syclobj_ :
273298 sua_iface ["syclobj" ] = self .syclobj_
299+ if self .transf_fn_ :
300+ sua_iface = self .transf_fn_ (sua_iface )
274301 return sua_iface
275302
276303
@@ -338,6 +365,106 @@ def test_suai_non_contig_2D(memory_ctor):
338365 assert np .array_equal (res , expected_res )
339366
340367
368+ def test_suai_invalid_suai ():
369+ n_bytes = 2 * 3 * 5 * 128
370+ try :
371+ q = dpctl .SyclQueue ()
372+ except dpctl .SyclQueueCreationError :
373+ pytest .skip ("Could not create default queue" )
374+ try :
375+ buf = MemoryUSMShared (n_bytes , queue = q )
376+ except Exception :
377+ pytest .skip ("USM-shared allocation failed" )
378+
379+ # different syclobj values
380+ class DuckSyclObject :
381+ def __init__ (self , syclobj ):
382+ self .syclobj = syclobj
383+
384+ def _get_capsule (self ):
385+ return self .syclobj ._get_capsule ()
386+
387+ ctx = q .sycl_context
388+ for syclobj in [
389+ q ,
390+ DuckSyclObject (q ),
391+ q ._get_capsule (),
392+ ctx ,
393+ DuckSyclObject (ctx ),
394+ ctx ._get_capsule (),
395+ ]:
396+ v = View (buf , shape = (n_bytes ,), strides = (1 ,), offset = 0 , syclobj = syclobj )
397+ MemoryUSMShared (v )
398+ with pytest .raises (ValueError ):
399+ MemoryUSMDevice (v )
400+ with pytest .raises (ValueError ):
401+ MemoryUSMHost (v )
402+
403+ # version validation
404+ def invalid_version (suai_iface ):
405+ "Set version to invalid"
406+ suai_iface ["version" ] = 0
407+ return suai_iface
408+
409+ v = View (
410+ buf , shape = (n_bytes ,), strides = (1 ,), offset = 0 , transf_fn = invalid_version
411+ )
412+ with pytest .raises (ValueError ):
413+ MemoryUSMShared (v )
414+
415+ # data validation
416+ def invalid_data (suai_iface ):
417+ "Set data to invalid"
418+ suai_iface ["data" ] = tuple ()
419+ return suai_iface
420+
421+ v = View (
422+ buf , shape = (n_bytes ,), strides = (1 ,), offset = 0 , transf_fn = invalid_data
423+ )
424+ with pytest .raises (ValueError ):
425+ MemoryUSMShared (v )
426+ # set shape to a negative value
427+ v = View (buf , shape = (- n_bytes ,), strides = (2 ,), offset = 0 )
428+ with pytest .raises (ValueError ):
429+ MemoryUSMShared (v )
430+ v = View (buf , shape = (- n_bytes ,), strides = None , offset = 0 )
431+ with pytest .raises (ValueError ):
432+ MemoryUSMShared (v )
433+ # shape validation
434+ v = View (buf , shape = None , strides = (1 ,), offset = 0 )
435+ with pytest .raises (ValueError ):
436+ MemoryUSMShared (v )
437+
438+ # typestr validation
439+ def invalid_typestr (suai_iface ):
440+ suai_iface ["typestr" ] = "invalid"
441+ return suai_iface
442+
443+ v = View (
444+ buf , shape = (n_bytes ,), strides = (1 ,), offset = 0 , transf_fn = invalid_typestr
445+ )
446+ with pytest .raises (ValueError ):
447+ MemoryUSMShared (v )
448+
449+ def unsupported_typestr (suai_iface ):
450+ suai_iface ["typestr" ] = "O"
451+ return suai_iface
452+
453+ v = View (
454+ buf ,
455+ shape = (n_bytes ,),
456+ strides = (1 ,),
457+ offset = 0 ,
458+ transf_fn = unsupported_typestr ,
459+ )
460+ with pytest .raises (ValueError ):
461+ MemoryUSMShared (v )
462+ # set strides to invalid value
463+ v = View (buf , shape = (n_bytes ,), strides = Ellipsis , offset = 0 )
464+ with pytest .raises (ValueError ):
465+ MemoryUSMShared (v )
466+
467+
341468def check_view (v ):
342469 """
343470 Memory object created from duck __sycl_usm_array_interface__ argument
@@ -389,3 +516,88 @@ def test_with_constructor(memory_ctor):
389516 syclobj = buf .sycl_device .filter_string ,
390517 )
391518 check_view (v )
519+
520+
521+ @pytest .mark .skipif (
522+ not has_sycl_platforms (),
523+ reason = "No SYCL devices except the default host device." ,
524+ )
525+ def test_cpython_api (memory_ctor ):
526+ import ctypes
527+ import sys
528+
529+ mobj = memory_ctor (1024 )
530+ mod = sys .modules [mobj .__class__ .__module__ ]
531+ # get capsules storing function pointers
532+ mem_ptr_fn_cap = mod .__pyx_capi__ ["get_usm_pointer" ]
533+ mem_ctx_fn_cap = mod .__pyx_capi__ ["get_context" ]
534+ mem_nby_fn_cap = mod .__pyx_capi__ ["get_nbytes" ]
535+ # construct Python callable to invoke "get_usm_pointer"
536+ cap_ptr_fn = ctypes .pythonapi .PyCapsule_GetPointer
537+ cap_ptr_fn .restype = ctypes .c_void_p
538+ cap_ptr_fn .argtypes = [ctypes .py_object , ctypes .c_char_p ]
539+ mem_ptr_fn_ptr = cap_ptr_fn (
540+ mem_ptr_fn_cap , b"DPCTLSyclUSMRef (struct Py_MemoryObject *)"
541+ )
542+ mem_ctx_fn_ptr = cap_ptr_fn (
543+ mem_ctx_fn_cap , b"DPCTLSyclContextRef (struct Py_MemoryObject *)"
544+ )
545+ mem_nby_fn_ptr = cap_ptr_fn (
546+ mem_nby_fn_cap , b"size_t (struct Py_MemoryObject *)"
547+ )
548+ callable_maker = ctypes .PYFUNCTYPE (ctypes .c_void_p , ctypes .py_object )
549+ get_ptr_fn = callable_maker (mem_ptr_fn_ptr )
550+ get_ctx_fn = callable_maker (mem_ctx_fn_ptr )
551+ get_nby_fn = callable_maker (mem_nby_fn_ptr )
552+
553+ capi_ptr = get_ptr_fn (mobj )
554+ direct_ptr = mobj ._pointer
555+ assert capi_ptr == direct_ptr
556+ capi_ctx_ref = get_ctx_fn (mobj )
557+ direct_ctx_ref = mobj ._context .addressof_ref ()
558+ assert capi_ctx_ref == direct_ctx_ref
559+ capi_nbytes = get_nby_fn (mobj )
560+ direct_nbytes = mobj .nbytes
561+ assert capi_nbytes == direct_nbytes
562+
563+
564+ def test_memory_construction_from_other_memory_objects ():
565+ try :
566+ q = dpctl .SyclQueue ()
567+ except dpctl .SyclQueueCreationError :
568+ pytest .skip ("Default queue could not be created" )
569+ m_sh = MemoryUSMShared (256 , queue = q )
570+ m_de = MemoryUSMDevice (256 , queue = q )
571+ m_ho = MemoryUSMHost (256 , queue = q )
572+ with pytest .raises (ValueError ):
573+ MemoryUSMDevice (m_sh )
574+ with pytest .raises (ValueError ):
575+ MemoryUSMHost (m_de )
576+ with pytest .raises (ValueError ):
577+ MemoryUSMShared (m_ho )
578+ m1 = MemoryUSMDevice (m_sh , copy = True )
579+ m2 = MemoryUSMHost (m_de , copy = True )
580+ m3 = MemoryUSMShared (m_de , copy = True )
581+ assert bytes (m1 ) == bytes (m_sh )
582+ assert bytes (m2 ) == bytes (m3 )
583+
584+
585+ def test_memory_copy_between_contexts ():
586+ try :
587+ q = dpctl .SyclQueue ("cpu" )
588+ except dpctl .SyclQueueCreationError :
589+ pytest .skip ("CPU queue could not be created" )
590+ d = q .sycl_device
591+ n = d .max_compute_units
592+ n_half = n // 2
593+ d0 , d1 = d .create_sub_devices (partition = [n_half , n - n_half ])
594+ q0 = dpctl .SyclQueue (d0 )
595+ q1 = dpctl .SyclQueue (d1 )
596+ m0 = MemoryUSMDevice (256 , queue = q0 )
597+ m1 = MemoryUSMDevice (256 , queue = q1 )
598+ host_buf = b"abcd" * 64
599+ m0 .copy_from_host (host_buf )
600+ m1 .copy_from_device (m0 )
601+ copy_buf = bytearray (256 )
602+ m1 .copy_to_host (copy_buf )
603+ assert host_buf == copy_buf
0 commit comments