xref: /petsc/src/sys/objects/device/tests/ex7.c (revision 6797ed33bfc5684cca29e5d9b9bbfc6c8aac93e0)
10e6b6b59SJacob Faibussowitsch static const char help[] = "Tests PetscDeviceAllocate().\n\n";
20e6b6b59SJacob Faibussowitsch 
30e6b6b59SJacob Faibussowitsch #include "petscdevicetestcommon.h"
40e6b6b59SJacob Faibussowitsch 
50e6b6b59SJacob Faibussowitsch #define DebugPrintf(comm, ...) PetscPrintf((comm), "[DEBUG OUTPUT] " __VA_ARGS__)
60e6b6b59SJacob Faibussowitsch 
70e6b6b59SJacob Faibussowitsch static PetscErrorCode IncrementSize(PetscRandom rand, PetscInt *value) {
80e6b6b59SJacob Faibussowitsch   PetscReal rval;
90e6b6b59SJacob Faibussowitsch 
100e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
110e6b6b59SJacob Faibussowitsch   // set the interval such that *value += rval never goes below 0 or above 500
120e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomSetInterval(rand, -(*value), 500 - (*value)));
130e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomGetValueReal(rand, &rval));
140e6b6b59SJacob Faibussowitsch   *value += (PetscInt)rval;
150e6b6b59SJacob Faibussowitsch   PetscCall(DebugPrintf(PetscObjectComm((PetscObject)rand), "n: %" PetscInt_FMT "\n", *value));
160e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
170e6b6b59SJacob Faibussowitsch }
180e6b6b59SJacob Faibussowitsch 
190e6b6b59SJacob Faibussowitsch static PetscErrorCode TestAllocate(PetscDeviceContext dctx, PetscRandom rand, PetscMemType mtype) {
200e6b6b59SJacob Faibussowitsch   PetscScalar *ptr, *tmp_ptr;
210e6b6b59SJacob Faibussowitsch   PetscInt     n = 10;
220e6b6b59SJacob Faibussowitsch 
230e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
240e6b6b59SJacob Faibussowitsch   if (PetscMemTypeDevice(mtype)) {
250e6b6b59SJacob Faibussowitsch     PetscDeviceType dtype;
260e6b6b59SJacob Faibussowitsch 
270e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
280e6b6b59SJacob Faibussowitsch     // host device context cannot handle this
290e6b6b59SJacob Faibussowitsch     if (dtype == PETSC_DEVICE_HOST) PetscFunctionReturn(0);
300e6b6b59SJacob Faibussowitsch   }
310e6b6b59SJacob Faibussowitsch   // test basic allocation, deallocation
320e6b6b59SJacob Faibussowitsch   PetscCall(IncrementSize(rand, &n));
330e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceMalloc(dctx, mtype, n, &ptr));
340e6b6b59SJacob Faibussowitsch   PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "PetscDeviceMalloc() return NULL pointer for %s allocation size %" PetscInt_FMT, PetscMemTypeToString(mtype), n);
35*6797ed33SJacob Faibussowitsch   // this ensures the host pointer is at least valid
360e6b6b59SJacob Faibussowitsch   if (PetscMemTypeHost(mtype)) {
370e6b6b59SJacob Faibussowitsch     for (PetscInt i = 0; i < n; ++i) ptr[i] = (PetscScalar)i;
380e6b6b59SJacob Faibussowitsch   }
390e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(dctx, ptr));
400e6b6b59SJacob Faibussowitsch 
41*6797ed33SJacob Faibussowitsch   // test alignment of various types
42*6797ed33SJacob Faibussowitsch   {
43*6797ed33SJacob Faibussowitsch     char     *char_ptr;
44*6797ed33SJacob Faibussowitsch     short    *short_ptr;
45*6797ed33SJacob Faibussowitsch     int      *int_ptr;
46*6797ed33SJacob Faibussowitsch     double   *double_ptr;
47*6797ed33SJacob Faibussowitsch     long int *long_int_ptr;
48*6797ed33SJacob Faibussowitsch 
49*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, mtype, 1, &char_ptr));
50*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, mtype, 1, &short_ptr));
51*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, mtype, 1, &int_ptr));
52*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, mtype, 1, &double_ptr));
53*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, mtype, 1, &long_int_ptr));
54*6797ed33SJacob Faibussowitsch 
55*6797ed33SJacob Faibussowitsch     // if an error occurs here, it means the alignment system is broken!
56*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, char_ptr));
57*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, short_ptr));
58*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, int_ptr));
59*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, double_ptr));
60*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, long_int_ptr));
61*6797ed33SJacob Faibussowitsch   }
62*6797ed33SJacob Faibussowitsch 
630e6b6b59SJacob Faibussowitsch   // test that calloc() produces cleared memory
640e6b6b59SJacob Faibussowitsch   PetscCall(IncrementSize(rand, &n));
650e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceCalloc(dctx, mtype, n, &ptr));
66*6797ed33SJacob Faibussowitsch   PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "PetscDeviceCalloc() returned NULL pointer for %s allocation size %" PetscInt_FMT, PetscMemTypeToString(mtype), n);
670e6b6b59SJacob Faibussowitsch   if (PetscMemTypeHost(mtype)) {
680e6b6b59SJacob Faibussowitsch     tmp_ptr = ptr;
690e6b6b59SJacob Faibussowitsch   } else {
700e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, PETSC_MEMTYPE_HOST, n, &tmp_ptr));
710e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceArrayCopy(dctx, tmp_ptr, ptr, n));
720e6b6b59SJacob Faibussowitsch   }
73*6797ed33SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx));
740e6b6b59SJacob Faibussowitsch   for (PetscInt i = 0; i < n; ++i) PetscCheck(tmp_ptr[i] == (PetscScalar)0.0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceCalloc() returned memory that was not cleared, ptr[%" PetscInt_FMT "] %g != 0", i, (double)PetscAbsScalar(tmp_ptr[i]));
750e6b6b59SJacob Faibussowitsch   if (tmp_ptr == ptr) {
760e6b6b59SJacob Faibussowitsch     tmp_ptr = NULL;
770e6b6b59SJacob Faibussowitsch   } else {
780e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceFree(dctx, tmp_ptr));
790e6b6b59SJacob Faibussowitsch   }
800e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(dctx, ptr));
810e6b6b59SJacob Faibussowitsch 
820e6b6b59SJacob Faibussowitsch   // test that devicearrayzero produces cleared memory
830e6b6b59SJacob Faibussowitsch   PetscCall(IncrementSize(rand, &n));
840e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceMalloc(dctx, mtype, n, &ptr));
850e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceArrayZero(dctx, ptr, n));
860e6b6b59SJacob Faibussowitsch   PetscCall(PetscMalloc1(n, &tmp_ptr));
870e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory(tmp_ptr, PETSC_MEMTYPE_HOST, n * sizeof(*tmp_ptr)));
880e6b6b59SJacob Faibussowitsch   for (PetscInt i = 0; i < n; ++i) tmp_ptr[i] = (PetscScalar)i;
890e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceArrayCopy(dctx, tmp_ptr, ptr, n));
900e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(dctx));
910e6b6b59SJacob Faibussowitsch   for (PetscInt i = 0; i < n; ++i) PetscCheck(tmp_ptr[i] == (PetscScalar)0.0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceArrayZero() did not not clear memory, ptr[%" PetscInt_FMT "] %g != 0", i, (double)PetscAbsScalar(tmp_ptr[i]));
920e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(dctx, tmp_ptr));
930e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(dctx, ptr));
940e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
950e6b6b59SJacob Faibussowitsch }
960e6b6b59SJacob Faibussowitsch 
970e6b6b59SJacob Faibussowitsch static PetscErrorCode TestAsyncCoherence(PetscDeviceContext dctx, PetscRandom rand) {
980e6b6b59SJacob Faibussowitsch   const PetscInt      nsub = 2;
990e6b6b59SJacob Faibussowitsch   const PetscInt      n    = 1024;
1000e6b6b59SJacob Faibussowitsch   PetscScalar        *ptr, *tmp_ptr;
1010e6b6b59SJacob Faibussowitsch   PetscDeviceType     dtype;
1020e6b6b59SJacob Faibussowitsch   PetscDeviceContext *sub;
1030e6b6b59SJacob Faibussowitsch 
1040e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1050e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
1060e6b6b59SJacob Faibussowitsch   // ensure the streams are nonblocking
1070e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextForkWithStreamType(dctx, PETSC_STREAM_GLOBAL_NONBLOCKING, nsub, &sub));
1080e6b6b59SJacob Faibussowitsch   // do a warmup to ensure each context acquires any necessary data structures
1090e6b6b59SJacob Faibussowitsch   for (PetscInt i = 0; i < nsub; ++i) {
1100e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(sub[i], PETSC_MEMTYPE_HOST, n, &ptr));
1110e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceFree(sub[i], ptr));
1120e6b6b59SJacob Faibussowitsch     if (dtype != PETSC_DEVICE_HOST) {
1130e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceMalloc(sub[i], PETSC_MEMTYPE_DEVICE, n, &ptr));
1140e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceFree(sub[i], ptr));
1150e6b6b59SJacob Faibussowitsch     }
1160e6b6b59SJacob Faibussowitsch   }
1170e6b6b59SJacob Faibussowitsch 
1180e6b6b59SJacob Faibussowitsch   // allocate on one
1190e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceMalloc(sub[0], PETSC_MEMTYPE_HOST, n, &ptr));
1200e6b6b59SJacob Faibussowitsch   // free on the other
1210e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(sub[1], ptr));
1220e6b6b59SJacob Faibussowitsch 
1230e6b6b59SJacob Faibussowitsch   // allocate on one
1240e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceMalloc(sub[0], PETSC_MEMTYPE_HOST, n, &ptr));
1250e6b6b59SJacob Faibussowitsch   // zero on the other
1260e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceArrayZero(sub[1], ptr, n));
1270e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextSynchronize(sub[1]));
1280e6b6b59SJacob Faibussowitsch   for (PetscInt i = 0; i < n; ++i) {
1290e6b6b59SJacob Faibussowitsch     for (PetscInt i = 0; i < n; ++i) PetscCheck(ptr[i] == (PetscScalar)0.0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceArrayZero() was not properly serialized, ptr[%" PetscInt_FMT "] %g != 0", i, (double)PetscAbsScalar(ptr[i]));
1300e6b6b59SJacob Faibussowitsch   }
1310e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceFree(sub[1], ptr));
1320e6b6b59SJacob Faibussowitsch 
1330e6b6b59SJacob Faibussowitsch   // test the transfers are serialized
1340e6b6b59SJacob Faibussowitsch   if (dtype != PETSC_DEVICE_HOST) {
1350e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceCalloc(dctx, PETSC_MEMTYPE_DEVICE, n, &ptr));
1360e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceMalloc(dctx, PETSC_MEMTYPE_HOST, n, &tmp_ptr));
1370e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceArrayCopy(sub[0], tmp_ptr, ptr, n));
1380e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextSynchronize(sub[0]));
1390e6b6b59SJacob Faibussowitsch     for (PetscInt i = 0; i < n; ++i) {
1400e6b6b59SJacob Faibussowitsch       for (PetscInt i = 0; i < n; ++i) PetscCheck(tmp_ptr[i] == (PetscScalar)0.0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceArrayCopt() was not properly serialized, ptr[%" PetscInt_FMT "] %g != 0", i, (double)PetscAbsScalar(tmp_ptr[i]));
1410e6b6b59SJacob Faibussowitsch     }
1420e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceFree(sub[1], ptr));
1430e6b6b59SJacob Faibussowitsch   }
1440e6b6b59SJacob Faibussowitsch 
1450e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextJoin(dctx, nsub, PETSC_DEVICE_CONTEXT_JOIN_DESTROY, &sub));
1460e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1470e6b6b59SJacob Faibussowitsch }
1480e6b6b59SJacob Faibussowitsch 
1490e6b6b59SJacob Faibussowitsch int main(int argc, char *argv[]) {
1500e6b6b59SJacob Faibussowitsch   PetscDeviceContext dctx;
1510e6b6b59SJacob Faibussowitsch   PetscRandom        rand;
1520e6b6b59SJacob Faibussowitsch 
1530e6b6b59SJacob Faibussowitsch   PetscFunctionBeginUser;
1540e6b6b59SJacob Faibussowitsch   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
1550e6b6b59SJacob Faibussowitsch 
1560e6b6b59SJacob Faibussowitsch   // A vile hack. The -info output is used to test correctness in this test which prints --
1570e6b6b59SJacob Faibussowitsch   // among other things -- the PetscObjectId of the PetscDevicContext and the allocated memory.
1580e6b6b59SJacob Faibussowitsch   //
1590e6b6b59SJacob Faibussowitsch   // Due to device and host creating slightly different number of objects on startup there will
1600e6b6b59SJacob Faibussowitsch   // be a mismatch in the ID's. So for the tests involving the host we sit here creating
1610e6b6b59SJacob Faibussowitsch   // PetscContainers (and incrementing the global PetscObjectId counter) until it reaches some
1620e6b6b59SJacob Faibussowitsch   // arbitrarily high number to ensure that our first PetscDeviceContext has the same ID across
1630e6b6b59SJacob Faibussowitsch   // systems.
1640e6b6b59SJacob Faibussowitsch   if (PETSC_DEVICE_DEFAULT() == PETSC_DEVICE_HOST) {
1650e6b6b59SJacob Faibussowitsch     PetscObjectId id, prev_id = 0;
1660e6b6b59SJacob Faibussowitsch 
1670e6b6b59SJacob Faibussowitsch     do {
1680e6b6b59SJacob Faibussowitsch       PetscContainer c;
1690e6b6b59SJacob Faibussowitsch 
1700e6b6b59SJacob Faibussowitsch       PetscCall(PetscContainerCreate(PETSC_COMM_WORLD, &c));
1710e6b6b59SJacob Faibussowitsch       PetscCall(PetscObjectGetId((PetscObject)c, &id));
1720e6b6b59SJacob Faibussowitsch       // sanity check, in case PetscContainer ever stops being a PetscObject
1730e6b6b59SJacob Faibussowitsch       PetscCheck(id > prev_id, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscObjectIds are not increasing for successively created PetscContainers! current: %" PetscInt64_FMT ", previous: %" PetscInt64_FMT, id, prev_id);
1740e6b6b59SJacob Faibussowitsch       prev_id = id;
1750e6b6b59SJacob Faibussowitsch       PetscCall(PetscContainerDestroy(&c));
1760e6b6b59SJacob Faibussowitsch     } while (id < 10);
1770e6b6b59SJacob Faibussowitsch   }
1780e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetCurrentContext(&dctx));
1790e6b6b59SJacob Faibussowitsch 
1800e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rand));
1810e6b6b59SJacob Faibussowitsch   // this seed just so happens to keep the allocation size increasing
1820e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomSetSeed(rand, 123));
1830e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomSeed(rand));
1840e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomSetFromOptions(rand));
1850e6b6b59SJacob Faibussowitsch 
1860e6b6b59SJacob Faibussowitsch   PetscCall(TestAllocate(dctx, rand, PETSC_MEMTYPE_HOST));
1870e6b6b59SJacob Faibussowitsch   PetscCall(TestAllocate(dctx, rand, PETSC_MEMTYPE_DEVICE));
1880e6b6b59SJacob Faibussowitsch   PetscCall(TestAsyncCoherence(dctx, rand));
1890e6b6b59SJacob Faibussowitsch 
1900e6b6b59SJacob Faibussowitsch   PetscCall(PetscRandomDestroy(&rand));
1910e6b6b59SJacob Faibussowitsch   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n"));
1920e6b6b59SJacob Faibussowitsch   PetscCall(PetscFinalize());
1930e6b6b59SJacob Faibussowitsch   return 0;
1940e6b6b59SJacob Faibussowitsch }
1950e6b6b59SJacob Faibussowitsch 
1960e6b6b59SJacob Faibussowitsch /*TEST
1970e6b6b59SJacob Faibussowitsch 
1980e6b6b59SJacob Faibussowitsch   build:
1990e6b6b59SJacob Faibussowitsch    requires: defined(PETSC_HAVE_CXX)
2000e6b6b59SJacob Faibussowitsch 
2010e6b6b59SJacob Faibussowitsch   testset:
2020e6b6b59SJacob Faibussowitsch    requires: defined(PETSC_USE_INFO), defined(PETSC_USE_DEBUG)
2030e6b6b59SJacob Faibussowitsch    args: -info :device
2040e6b6b59SJacob Faibussowitsch    suffix: with_info
2050e6b6b59SJacob Faibussowitsch    test:
2060e6b6b59SJacob Faibussowitsch      requires: !device
2070e6b6b59SJacob Faibussowitsch      suffix: host_no_device
2080e6b6b59SJacob Faibussowitsch    test:
2090e6b6b59SJacob Faibussowitsch      requires: device
2100e6b6b59SJacob Faibussowitsch      args: -default_device_type host
2110e6b6b59SJacob Faibussowitsch      filter: sed -e 's/host/IMPL/g' -e 's/cuda/IMPL/g' -e 's/hip/IMPL/g' -e 's/sycl/IMPL/g'
2120e6b6b59SJacob Faibussowitsch      suffix: host_with_device
2130e6b6b59SJacob Faibussowitsch    test:
2140e6b6b59SJacob Faibussowitsch      requires: cuda
2150e6b6b59SJacob Faibussowitsch      args: -default_device_type cuda
2160e6b6b59SJacob Faibussowitsch      suffix: cuda
2170e6b6b59SJacob Faibussowitsch    test:
2180e6b6b59SJacob Faibussowitsch      requires: hip
2190e6b6b59SJacob Faibussowitsch      args: -default_device_type hip
2200e6b6b59SJacob Faibussowitsch      suffix: hip
2210e6b6b59SJacob Faibussowitsch    test:
2220e6b6b59SJacob Faibussowitsch      requires: sycl
2230e6b6b59SJacob Faibussowitsch      args: -default_device_type sycl
2240e6b6b59SJacob Faibussowitsch      suffix: sycl
2250e6b6b59SJacob Faibussowitsch 
2260e6b6b59SJacob Faibussowitsch   testset:
2270e6b6b59SJacob Faibussowitsch    output_file: ./output/ExitSuccess.out
2280e6b6b59SJacob Faibussowitsch    requires: !defined(PETSC_USE_DEBUG)
2290e6b6b59SJacob Faibussowitsch    filter: grep -v "\[DEBUG OUTPUT\]"
2300e6b6b59SJacob Faibussowitsch    suffix: no_info
2310e6b6b59SJacob Faibussowitsch    test:
2320e6b6b59SJacob Faibussowitsch      requires: !device
2330e6b6b59SJacob Faibussowitsch      suffix: host_no_device
2340e6b6b59SJacob Faibussowitsch    test:
2350e6b6b59SJacob Faibussowitsch      requires: device
2360e6b6b59SJacob Faibussowitsch      args: -default_device_type host
2370e6b6b59SJacob Faibussowitsch      suffix: host_with_device
2380e6b6b59SJacob Faibussowitsch    test:
2390e6b6b59SJacob Faibussowitsch      requires: cuda
2400e6b6b59SJacob Faibussowitsch      args: -default_device_type cuda
2410e6b6b59SJacob Faibussowitsch      suffix: cuda
2420e6b6b59SJacob Faibussowitsch    test:
2430e6b6b59SJacob Faibussowitsch      requires: hip
2440e6b6b59SJacob Faibussowitsch      args: -default_device_type hip
2450e6b6b59SJacob Faibussowitsch      suffix: hip
2460e6b6b59SJacob Faibussowitsch    test:
2470e6b6b59SJacob Faibussowitsch      requires: sycl
2480e6b6b59SJacob Faibussowitsch      args: -default_device_type sycl
2490e6b6b59SJacob Faibussowitsch      suffix: sycl
2500e6b6b59SJacob Faibussowitsch TEST*/
251