@@ -48,12 +48,20 @@ def __sycl_usm_array_interface(self):
48
48
not has_sycl_platforms (),
49
49
reason = "No SYCL devices except the default host device." ,
50
50
)
51
- def test_memory_create ():
51
+ def test_memory_create (memory_ctor ):
52
+ import sys
53
+
52
54
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 )
55
57
assert mobj .nbytes == nbytes
56
58
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
57
65
58
66
59
67
@pytest .mark .skipif (
@@ -69,7 +77,7 @@ def test_memory_create_with_np():
69
77
70
78
def _create_memory ():
71
79
nbytes = 1024
72
- queue = dpctl .get_current_queue ()
80
+ queue = dpctl .SyclQueue ()
73
81
mobj = MemoryUSMShared (nbytes , alignment = 64 , queue = queue )
74
82
return mobj
75
83
@@ -90,38 +98,36 @@ def test_memory_without_context():
90
98
91
99
# Without context
92
100
assert mobj .get_usm_type () == "shared"
101
+ assert mobj .get_usm_type (syclobj = dpctl .SyclContext ()) == "shared"
93
102
94
103
95
104
@pytest .mark .skipif (not has_cpu (), reason = "No SYCL CPU device available." )
96
105
def test_memory_cpu_context ():
97
106
mobj = _create_memory ()
98
107
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"
105
112
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" ]
112
119
113
120
114
121
@pytest .mark .skipif (not has_gpu (), reason = "No OpenCL GPU queues available" )
115
122
def test_memory_gpu_context ():
116
123
mobj = _create_memory ()
117
124
118
125
# 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" ]
125
131
126
132
127
133
@pytest .mark .skipif (
@@ -166,10 +172,10 @@ def test_zero_copy():
166
172
not has_sycl_platforms (),
167
173
reason = "No SYCL devices except the default host device." ,
168
174
)
169
- def test_pickling ():
175
+ def test_pickling (memory_ctor ):
170
176
import pickle
171
177
172
- mobj = _create_memory ( )
178
+ mobj = memory_ctor ( 1024 , alignment = 64 )
173
179
host_src_obj = _create_host_buf (mobj .nbytes )
174
180
mobj .copy_from_host (host_src_obj )
175
181
@@ -185,6 +191,22 @@ def test_pickling():
185
191
), "Pickling/unpickling should be changing pointer"
186
192
187
193
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
+
188
210
@pytest .fixture (params = [MemoryUSMShared , MemoryUSMDevice , MemoryUSMHost ])
189
211
def memory_ctor (request ):
190
212
return request .param
@@ -256,12 +278,15 @@ def test_sycl_usm_array_interface(memory_ctor):
256
278
257
279
258
280
class 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
+ ):
260
284
self .buffer_ = buf
261
285
self .shape_ = shape
262
286
self .strides_ = strides
263
287
self .offset_ = offset
264
288
self .syclobj_ = syclobj
289
+ self .transf_fn_ = transf_fn
265
290
266
291
@property
267
292
def __sycl_usm_array_interface__ (self ):
@@ -271,6 +296,8 @@ def __sycl_usm_array_interface__(self):
271
296
sua_iface ["strides" ] = self .strides_
272
297
if self .syclobj_ :
273
298
sua_iface ["syclobj" ] = self .syclobj_
299
+ if self .transf_fn_ :
300
+ sua_iface = self .transf_fn_ (sua_iface )
274
301
return sua_iface
275
302
276
303
@@ -338,6 +365,106 @@ def test_suai_non_contig_2D(memory_ctor):
338
365
assert np .array_equal (res , expected_res )
339
366
340
367
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
+
341
468
def check_view (v ):
342
469
"""
343
470
Memory object created from duck __sycl_usm_array_interface__ argument
@@ -389,3 +516,88 @@ def test_with_constructor(memory_ctor):
389
516
syclobj = buf .sycl_device .filter_string ,
390
517
)
391
518
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