From 957b2b7e56d444f2179bcad6e39ca9f2c8e2bc83 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Mon, 25 Nov 2024 14:47:43 +0100 Subject: [PATCH 01/13] First draft to add support for sycl accessors: Added a function to add sycl accessors from a ps.field. Extended the parrallel_for to make it possible add more statements before the actual kernel call --- src/pystencilssfg/extensions/sycl.py | 8 ++- src/pystencilssfg/lang/cpp/sycl_accessor.py | 77 +++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/pystencilssfg/lang/cpp/sycl_accessor.py diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index 3cb0c1c..bb99101 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -6,6 +6,8 @@ import re from pystencils.types import PsType, PsCustomType from pystencils.enums import Target +from pystencilssfg.composer.basic_composer import SequencerArg + from ..exceptions import SfgException from ..context import SfgContext from ..composer import ( @@ -13,6 +15,7 @@ from ..composer import ( SfgClassComposer, SfgComposer, SfgComposerMixIn, + make_sequence, ) from ..ir.source_components import SfgKernelHandle, SfgHeaderInclude from ..ir import ( @@ -56,12 +59,13 @@ class SyclHandler(AugExpr): self._ctx = ctx - def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle): + def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle, *, extras: Sequence[SequencerArg]=[]): """Generate a ``parallel_for`` kernel invocation using this command group handler. Args: range: Object, or tuple of integers, indicating the kernel's iteration range kernel: Handle to the pystencils-kernel to be executed + extras: Statements that should be in the parallel_for but before the kernel call """ self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) @@ -81,7 +85,7 @@ class SyclHandler(AugExpr): id_param = list(filter(filter_id, kernel.scalar_parameters))[0] - tree = SfgKernelCallNode(kernel) + tree = make_sequence(*extras, SfgKernelCallNode(kernel)) kernel_lambda = SfgLambda(("=",), (id_param,), tree, None) return SyclKernelInvoke(self, SyclInvokeType.ParallelFor, range, kernel_lambda) diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py new file mode 100644 index 0000000..c6cd011 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -0,0 +1,77 @@ +from ...lang import SrcField, IFieldExtraction +from ...ir.source_components import SfgHeaderInclude + +from pystencils import Field +from pystencils.types import ( + PsType, + PsCustomType, +) + +from pystencilssfg.lang.expressions import AugExpr + + +class SyclAccessor(SrcField): + def __init__( + self, + T: PsType, + dimensions: int, + reference: bool = False, + ): + cpp_typestr = T.c_string() + if dimensions not in [1, 2, 3]: + raise ValueError("sycl accessors can only have dims 1, 2 or 3") + typestring = ( + f"sycl::accessor< {cpp_typestr}, {dimensions} > {'&' if reference else ''}" + ) + super().__init__(PsCustomType(typestring)) + + self._dim = dimensions + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return {SfgHeaderInclude("sycl/sycl.hpp", system_header=True)} + + def get_extraction(self) -> IFieldExtraction: + accessor = self + + class Extraction(IFieldExtraction): + def ptr(self) -> AugExpr: + return AugExpr.format( + "{}.get_multi_ptr<sycl::access::decorated::no>().get()", + accessor, + ) + + def size(self, coordinate: int) -> AugExpr | None: + if coordinate > accessor._dim: + return None + else: + return AugExpr.format( + "{}.get_range().get({})", accessor, coordinate + ) + + def stride(self, coordinate: int) -> AugExpr | None: + if coordinate > accessor._dim: + return None + else: + if coordinate == accessor._dim - 1: + return AugExpr.format("1") + else: + exprs = [] + args = [] + for d in range(coordinate + 1, accessor._dim): + args.extend([accessor, d]) + exprs.append("{}.get_range().get({})") + expr = " * ".join(exprs) + return AugExpr.format(expr, *args) + + return Extraction() + + +def sycl_accessor_ref(field: Field): + """Creates a `sycl::accessor &` for a given pystencils field.""" + + return SyclAccessor( + field.dtype, + field.spatial_dimensions, + reference=True, + ).var(field.name) -- GitLab From 670d03c26967a5cbca6a6b700095a3206d7bd44d Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Mon, 25 Nov 2024 15:07:09 +0100 Subject: [PATCH 02/13] formatting --- src/pystencilssfg/extensions/sycl.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index bb99101..2fdf0b1 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -59,7 +59,13 @@ class SyclHandler(AugExpr): self._ctx = ctx - def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle, *, extras: Sequence[SequencerArg]=[]): + def parallel_for( + self, + range: SfgVar | Sequence[int], + kernel: SfgKernelHandle, + *, + extras: Sequence[SequencerArg] = [], + ): """Generate a ``parallel_for`` kernel invocation using this command group handler. Args: -- GitLab From 9672cf5758b29d8793e5bdf63ee021086735dca8 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Tue, 26 Nov 2024 16:00:12 +0100 Subject: [PATCH 03/13] reworked the api for parallel_for to look like the sfg.function api and extended the sycl_accessor mapping to handle index dimensions --- .gitignore | 3 +- integration/test_sycl_buffer.py | 34 +++++++++++++++ src/pystencilssfg/extensions/sycl.py | 48 ++++++++++++++------- src/pystencilssfg/lang/cpp/sycl_accessor.py | 41 ++++++++++++------ 4 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 integration/test_sycl_buffer.py diff --git a/.gitignore b/.gitignore index 65c4064..399dabe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # dev environment **/.venv +**/venv # build artifacts dist @@ -22,4 +23,4 @@ htmlcov coverage.xml # mkdocs -site \ No newline at end of file +site diff --git a/integration/test_sycl_buffer.py b/integration/test_sycl_buffer.py new file mode 100644 index 0000000..7839b9f --- /dev/null +++ b/integration/test_sycl_buffer.py @@ -0,0 +1,34 @@ +from pystencils import Target, CreateKernelConfig, no_jit +from lbmpy import create_lb_update_rule, LBMOptimisation +from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgOutputMode +from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref +import pystencilssfg.extensions.sycl as sycl +from itertools import chain + +sfg_config = SfgConfiguration( + output_directory="out/test_sycl_buffer", + outer_namespace="gen_code", + impl_extension="ipp", + output_mode=SfgOutputMode.INLINE, +) + +with SourceFileGenerator(sfg_config) as sfg: + sfg = sycl.SyclComposer(sfg) + gen_config = CreateKernelConfig(target=Target.SYCL, jit=no_jit) + opt = LBMOptimisation(field_layout="fzyx") + update = create_lb_update_rule(lbm_optimisation=opt) + kernel = sfg.kernels.create(update, "lbm_update", gen_config) + + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(update.method.dim, "range") + mappings = [ + sfg.map_field(field, sycl_accessor_ref(field)) + for field in chain(update.free_fields, update.bound_fields) + ] + + sfg.function("lb_update")( + cgh.parallel_for(rang)( + *mappings, + sfg.call(kernel), + ), + ) diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index 2fdf0b1..2f3591c 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -62,24 +62,29 @@ class SyclHandler(AugExpr): def parallel_for( self, range: SfgVar | Sequence[int], - kernel: SfgKernelHandle, - *, - extras: Sequence[SequencerArg] = [], ): """Generate a ``parallel_for`` kernel invocation using this command group handler. + The syntax of this uses a chain of two calls to mimic C++ syntax: + + .. code-block:: Python + + sfg.parallel_for(range)( + # Body + ) + + The body is constructed via sequencing (see `make_sequence`). Args: range: Object, or tuple of integers, indicating the kernel's iteration range - kernel: Handle to the pystencils-kernel to be executed - extras: Statements that should be in the parallel_for but before the kernel call """ self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) - kfunc = kernel.get_kernel_function() - if kfunc.target != Target.SYCL: - raise SfgException( - f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" - ) + def check_kernel(kernel: SfgKernelHandle): + kfunc = kernel.get_kernel_function() + if kfunc.target != Target.SYCL: + raise SfgException( + f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" + ) id_regex = re.compile(r"sycl::(id|item|nd_item)<\s*[0-9]\s*>") @@ -89,12 +94,25 @@ class SyclHandler(AugExpr): and id_regex.search(param.dtype.c_string()) is not None ) - id_param = list(filter(filter_id, kernel.scalar_parameters))[0] - - tree = make_sequence(*extras, SfgKernelCallNode(kernel)) + def sequencer(*args: SequencerArg): + id_param = [] + for arg in args: + if isinstance(arg, SfgKernelCallNode): + check_kernel(arg._kernel_handle) + id_param.append(list(filter(filter_id, arg._kernel_handle.scalar_parameters))[0]) + + if not all(item == id_param[0] for item in id_param): + raise ValueError( + "id_param should be the same for all kernels in parallel_for" + ) + tree = make_sequence(*args) + + kernel_lambda = SfgLambda(("=",), (id_param[0],), tree, None) + return SyclKernelInvoke( + self, SyclInvokeType.ParallelFor, range, kernel_lambda + ) - kernel_lambda = SfgLambda(("=",), (id_param,), tree, None) - return SyclKernelInvoke(self, SyclInvokeType.ParallelFor, range, kernel_lambda) + return sequencer class SyclGroup(AugExpr): diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py index c6cd011..e8618a5 100644 --- a/src/pystencilssfg/lang/cpp/sycl_accessor.py +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -1,5 +1,7 @@ +import math from ...lang import SrcField, IFieldExtraction from ...ir.source_components import SfgHeaderInclude +from typing import Sequence from pystencils import Field from pystencils.types import ( @@ -15,6 +17,7 @@ class SyclAccessor(SrcField): self, T: PsType, dimensions: int, + index_shape: Sequence[int], reference: bool = False, ): cpp_typestr = T.c_string() @@ -25,7 +28,11 @@ class SyclAccessor(SrcField): ) super().__init__(PsCustomType(typestring)) - self._dim = dimensions + self._spatial_dimensions = dimensions + self._index_dimensions = len(index_shape) + self._index_shape = index_shape + self._index_size = math.prod(index_shape) + self._total_dimensions_ = self._spatial_dimensions + self._index_dimensions @property def required_includes(self) -> set[SfgHeaderInclude]: @@ -42,7 +49,7 @@ class SyclAccessor(SrcField): ) def size(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._dim: + if coordinate > accessor._spatial_dimensions: return None else: return AugExpr.format( @@ -50,28 +57,36 @@ class SyclAccessor(SrcField): ) def stride(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._dim: + if coordinate > accessor._total_dimensions_: return None + elif coordinate >= accessor._spatial_dimensions - 1: + start = (coordinate - accessor._spatial_dimensions) + 1 + return AugExpr.format( + "{}", math.prod(accessor._index_shape[start:]) + ) else: - if coordinate == accessor._dim - 1: - return AugExpr.format("1") - else: - exprs = [] - args = [] - for d in range(coordinate + 1, accessor._dim): - args.extend([accessor, d]) - exprs.append("{}.get_range().get({})") - expr = " * ".join(exprs) - return AugExpr.format(expr, *args) + exprs = [] + args = [] + for d in range(coordinate + 1, accessor._spatial_dimensions): + args.extend([accessor, d]) + exprs.append("{}.get_range().get({})") + expr = " * ".join(exprs) + expr += " * {}" + return AugExpr.format(expr, *args, accessor._index_size) return Extraction() def sycl_accessor_ref(field: Field): """Creates a `sycl::accessor &` for a given pystencils field.""" + # Sycl accesors allow only at max 3 dimensions: + # So also mapping the index dimens to the sycl accesor we only can have 2D LBM stuff + # In principle it would be possible to map it to something like sycl::buffer<std::array<double, 19>, 3> + # but then would need to generate kernels that have sycl accessors as arguments return SyclAccessor( field.dtype, field.spatial_dimensions, + field.index_shape, reference=True, ).var(field.name) -- GitLab From ef144a22de027eff64dc25e7180656c17dec2053 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 28 Nov 2024 13:19:15 +0100 Subject: [PATCH 04/13] All dimensions of a field are now mapped to the accessor dimension, this allows only only 3 dims at maximum (spatial and index dim ) add some tests --- src/pystencilssfg/lang/cpp/sycl_accessor.py | 37 ++++++------------- tests/lang/test_sycl.py | 41 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 tests/lang/test_sycl.py diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py index e8618a5..f704477 100644 --- a/src/pystencilssfg/lang/cpp/sycl_accessor.py +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -1,7 +1,5 @@ -import math from ...lang import SrcField, IFieldExtraction from ...ir.source_components import SfgHeaderInclude -from typing import Sequence from pystencils import Field from pystencils.types import ( @@ -17,22 +15,17 @@ class SyclAccessor(SrcField): self, T: PsType, dimensions: int, - index_shape: Sequence[int], reference: bool = False, ): cpp_typestr = T.c_string() - if dimensions not in [1, 2, 3]: + if 3 < dimensions: raise ValueError("sycl accessors can only have dims 1, 2 or 3") typestring = ( f"sycl::accessor< {cpp_typestr}, {dimensions} > {'&' if reference else ''}" ) super().__init__(PsCustomType(typestring)) - - self._spatial_dimensions = dimensions - self._index_dimensions = len(index_shape) - self._index_shape = index_shape - self._index_size = math.prod(index_shape) - self._total_dimensions_ = self._spatial_dimensions + self._index_dimensions + self._dim = dimensions + self._inner_stride = 1 @property def required_includes(self) -> set[SfgHeaderInclude]: @@ -49,7 +42,7 @@ class SyclAccessor(SrcField): ) def size(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._spatial_dimensions: + if coordinate > accessor._dim: return None else: return AugExpr.format( @@ -57,36 +50,30 @@ class SyclAccessor(SrcField): ) def stride(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._total_dimensions_: + if coordinate > accessor._dim: return None - elif coordinate >= accessor._spatial_dimensions - 1: - start = (coordinate - accessor._spatial_dimensions) + 1 - return AugExpr.format( - "{}", math.prod(accessor._index_shape[start:]) - ) + elif coordinate == accessor._dim - 1: + return AugExpr.format("{}", accessor._inner_stride) else: exprs = [] args = [] - for d in range(coordinate + 1, accessor._spatial_dimensions): + for d in range(coordinate + 1, accessor._dim): args.extend([accessor, d]) exprs.append("{}.get_range().get({})") expr = " * ".join(exprs) expr += " * {}" - return AugExpr.format(expr, *args, accessor._index_size) + return AugExpr.format(expr, *args, accessor._inner_stride) return Extraction() def sycl_accessor_ref(field: Field): """Creates a `sycl::accessor &` for a given pystencils field.""" - # Sycl accesors allow only at max 3 dimensions: - # So also mapping the index dimens to the sycl accesor we only can have 2D LBM stuff - # In principle it would be possible to map it to something like sycl::buffer<std::array<double, 19>, 3> - # but then would need to generate kernels that have sycl accessors as arguments + # Sycl Accessor do not expose information about strides, so the linearization is like here + # https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#_multi_dimensional_objects_and_linearization return SyclAccessor( field.dtype, - field.spatial_dimensions, - field.index_shape, + field.spatial_dimensions + field.index_dimensions, reference=True, ).var(field.name) diff --git a/tests/lang/test_sycl.py b/tests/lang/test_sycl.py new file mode 100644 index 0000000..88f958f --- /dev/null +++ b/tests/lang/test_sycl.py @@ -0,0 +1,41 @@ +import pytest + + +from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref +import pystencils as ps + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +@pytest.mark.parametrize("dim", [1, 2, 3]) +def test_spatial_field(data_type, dim): + f = ps.fields(f"f:{data_type}[{dim}D]") + ref = sycl_accessor_ref(f) + assert ( + f"sycl::accessor< {data_type}, {dim} > &" in ref.as_variable().name_and_type() + ) + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +def test_to_large_dim(data_type): + dim = 4 + f = ps.fields(f"f:{data_type}[{dim}D]") + with pytest.raises(ValueError): + sycl_accessor_ref(f) + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +@pytest.mark.parametrize("spatial_dim", [1, 2, 3]) +@pytest.mark.parametrize("index_dims", [1, 2, 3]) +def test_index_field(data_type, spatial_dim, index_dims): + index_shape = ("19",) * index_dims + total_dims = spatial_dim + index_dims + f = ps.fields(f"f({', '.join(index_shape)}):{data_type}[{spatial_dim}D]") + if total_dims <= 3: + ref = sycl_accessor_ref(f) + assert ( + f"sycl::accessor< {data_type}, {total_dims} > &" + in ref.as_variable().name_and_type() + ) + else: + with pytest.raises(ValueError): + sycl_accessor_ref(f) -- GitLab From d862626f27396de12643ddfdbbd3316217b98845 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 28 Nov 2024 13:54:14 +0100 Subject: [PATCH 05/13] added a test for the parallel_for --- tests/extensions/test_sycl.py | 64 +++++++++++++++++++ .../{test_sycl.py => test_sycl_accessor.py} | 0 2 files changed, 64 insertions(+) create mode 100644 tests/extensions/test_sycl.py rename tests/lang/{test_sycl.py => test_sycl_accessor.py} (100%) diff --git a/tests/extensions/test_sycl.py b/tests/extensions/test_sycl.py new file mode 100644 index 0000000..db99278 --- /dev/null +++ b/tests/extensions/test_sycl.py @@ -0,0 +1,64 @@ +import pytest +from pystencilssfg import SourceFileGenerator +import pystencilssfg.extensions.sycl as sycl +import pystencils as ps +from pystencilssfg import SfgContext + + +def test_parallel_for_1_kernels(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g, h, i = ps.fields(f"f,g,h,i:{data_type}[{dim}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) + + +def test_parallel_for_2_kernels(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g, h, i = ps.fields(f"f,g,h,i:{data_type}[{dim}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) + + +def test_parallel_for_2_kernels_fail(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g = ps.fields(f"f,g:{data_type}[{dim}D]") + h, i = ps.fields(f"h,i:{data_type}[{dim-1}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + with pytest.raises(ValueError): + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) diff --git a/tests/lang/test_sycl.py b/tests/lang/test_sycl_accessor.py similarity index 100% rename from tests/lang/test_sycl.py rename to tests/lang/test_sycl_accessor.py -- GitLab From 218c3334d922785836e40cd033172ec0e55a1891 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Mon, 25 Nov 2024 14:47:43 +0100 Subject: [PATCH 06/13] First draft to add support for sycl accessors: Added a function to add sycl accessors from a ps.field. Extended the parrallel_for to make it possible add more statements before the actual kernel call --- src/pystencilssfg/extensions/sycl.py | 8 ++- src/pystencilssfg/lang/cpp/sycl_accessor.py | 77 +++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/pystencilssfg/lang/cpp/sycl_accessor.py diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index 3cb0c1c..bb99101 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -6,6 +6,8 @@ import re from pystencils.types import PsType, PsCustomType from pystencils.enums import Target +from pystencilssfg.composer.basic_composer import SequencerArg + from ..exceptions import SfgException from ..context import SfgContext from ..composer import ( @@ -13,6 +15,7 @@ from ..composer import ( SfgClassComposer, SfgComposer, SfgComposerMixIn, + make_sequence, ) from ..ir.source_components import SfgKernelHandle, SfgHeaderInclude from ..ir import ( @@ -56,12 +59,13 @@ class SyclHandler(AugExpr): self._ctx = ctx - def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle): + def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle, *, extras: Sequence[SequencerArg]=[]): """Generate a ``parallel_for`` kernel invocation using this command group handler. Args: range: Object, or tuple of integers, indicating the kernel's iteration range kernel: Handle to the pystencils-kernel to be executed + extras: Statements that should be in the parallel_for but before the kernel call """ self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) @@ -81,7 +85,7 @@ class SyclHandler(AugExpr): id_param = list(filter(filter_id, kernel.scalar_parameters))[0] - tree = SfgKernelCallNode(kernel) + tree = make_sequence(*extras, SfgKernelCallNode(kernel)) kernel_lambda = SfgLambda(("=",), (id_param,), tree, None) return SyclKernelInvoke(self, SyclInvokeType.ParallelFor, range, kernel_lambda) diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py new file mode 100644 index 0000000..c6cd011 --- /dev/null +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -0,0 +1,77 @@ +from ...lang import SrcField, IFieldExtraction +from ...ir.source_components import SfgHeaderInclude + +from pystencils import Field +from pystencils.types import ( + PsType, + PsCustomType, +) + +from pystencilssfg.lang.expressions import AugExpr + + +class SyclAccessor(SrcField): + def __init__( + self, + T: PsType, + dimensions: int, + reference: bool = False, + ): + cpp_typestr = T.c_string() + if dimensions not in [1, 2, 3]: + raise ValueError("sycl accessors can only have dims 1, 2 or 3") + typestring = ( + f"sycl::accessor< {cpp_typestr}, {dimensions} > {'&' if reference else ''}" + ) + super().__init__(PsCustomType(typestring)) + + self._dim = dimensions + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return {SfgHeaderInclude("sycl/sycl.hpp", system_header=True)} + + def get_extraction(self) -> IFieldExtraction: + accessor = self + + class Extraction(IFieldExtraction): + def ptr(self) -> AugExpr: + return AugExpr.format( + "{}.get_multi_ptr<sycl::access::decorated::no>().get()", + accessor, + ) + + def size(self, coordinate: int) -> AugExpr | None: + if coordinate > accessor._dim: + return None + else: + return AugExpr.format( + "{}.get_range().get({})", accessor, coordinate + ) + + def stride(self, coordinate: int) -> AugExpr | None: + if coordinate > accessor._dim: + return None + else: + if coordinate == accessor._dim - 1: + return AugExpr.format("1") + else: + exprs = [] + args = [] + for d in range(coordinate + 1, accessor._dim): + args.extend([accessor, d]) + exprs.append("{}.get_range().get({})") + expr = " * ".join(exprs) + return AugExpr.format(expr, *args) + + return Extraction() + + +def sycl_accessor_ref(field: Field): + """Creates a `sycl::accessor &` for a given pystencils field.""" + + return SyclAccessor( + field.dtype, + field.spatial_dimensions, + reference=True, + ).var(field.name) -- GitLab From 188308e1ea63bb84a1f094723a907dc7300fb941 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Mon, 25 Nov 2024 15:07:09 +0100 Subject: [PATCH 07/13] formatting --- src/pystencilssfg/extensions/sycl.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index bb99101..2fdf0b1 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -59,7 +59,13 @@ class SyclHandler(AugExpr): self._ctx = ctx - def parallel_for(self, range: SfgVar | Sequence[int], kernel: SfgKernelHandle, *, extras: Sequence[SequencerArg]=[]): + def parallel_for( + self, + range: SfgVar | Sequence[int], + kernel: SfgKernelHandle, + *, + extras: Sequence[SequencerArg] = [], + ): """Generate a ``parallel_for`` kernel invocation using this command group handler. Args: -- GitLab From 26dc9e70e2dba9290f6916da96369215d7c84b5c Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Tue, 26 Nov 2024 16:00:12 +0100 Subject: [PATCH 08/13] reworked the api for parallel_for to look like the sfg.function api and extended the sycl_accessor mapping to handle index dimensions --- .gitignore | 3 +- integration/test_sycl_buffer.py | 34 +++++++++++++++ src/pystencilssfg/extensions/sycl.py | 48 ++++++++++++++------- src/pystencilssfg/lang/cpp/sycl_accessor.py | 41 ++++++++++++------ 4 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 integration/test_sycl_buffer.py diff --git a/.gitignore b/.gitignore index 65c4064..399dabe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # dev environment **/.venv +**/venv # build artifacts dist @@ -22,4 +23,4 @@ htmlcov coverage.xml # mkdocs -site \ No newline at end of file +site diff --git a/integration/test_sycl_buffer.py b/integration/test_sycl_buffer.py new file mode 100644 index 0000000..7839b9f --- /dev/null +++ b/integration/test_sycl_buffer.py @@ -0,0 +1,34 @@ +from pystencils import Target, CreateKernelConfig, no_jit +from lbmpy import create_lb_update_rule, LBMOptimisation +from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgOutputMode +from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref +import pystencilssfg.extensions.sycl as sycl +from itertools import chain + +sfg_config = SfgConfiguration( + output_directory="out/test_sycl_buffer", + outer_namespace="gen_code", + impl_extension="ipp", + output_mode=SfgOutputMode.INLINE, +) + +with SourceFileGenerator(sfg_config) as sfg: + sfg = sycl.SyclComposer(sfg) + gen_config = CreateKernelConfig(target=Target.SYCL, jit=no_jit) + opt = LBMOptimisation(field_layout="fzyx") + update = create_lb_update_rule(lbm_optimisation=opt) + kernel = sfg.kernels.create(update, "lbm_update", gen_config) + + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(update.method.dim, "range") + mappings = [ + sfg.map_field(field, sycl_accessor_ref(field)) + for field in chain(update.free_fields, update.bound_fields) + ] + + sfg.function("lb_update")( + cgh.parallel_for(rang)( + *mappings, + sfg.call(kernel), + ), + ) diff --git a/src/pystencilssfg/extensions/sycl.py b/src/pystencilssfg/extensions/sycl.py index 2fdf0b1..2f3591c 100644 --- a/src/pystencilssfg/extensions/sycl.py +++ b/src/pystencilssfg/extensions/sycl.py @@ -62,24 +62,29 @@ class SyclHandler(AugExpr): def parallel_for( self, range: SfgVar | Sequence[int], - kernel: SfgKernelHandle, - *, - extras: Sequence[SequencerArg] = [], ): """Generate a ``parallel_for`` kernel invocation using this command group handler. + The syntax of this uses a chain of two calls to mimic C++ syntax: + + .. code-block:: Python + + sfg.parallel_for(range)( + # Body + ) + + The body is constructed via sequencing (see `make_sequence`). Args: range: Object, or tuple of integers, indicating the kernel's iteration range - kernel: Handle to the pystencils-kernel to be executed - extras: Statements that should be in the parallel_for but before the kernel call """ self._ctx.add_include(SfgHeaderInclude("sycl/sycl.hpp", system_header=True)) - kfunc = kernel.get_kernel_function() - if kfunc.target != Target.SYCL: - raise SfgException( - f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" - ) + def check_kernel(kernel: SfgKernelHandle): + kfunc = kernel.get_kernel_function() + if kfunc.target != Target.SYCL: + raise SfgException( + f"Kernel given to `parallel_for` is no SYCL kernel: {kernel.kernel_name}" + ) id_regex = re.compile(r"sycl::(id|item|nd_item)<\s*[0-9]\s*>") @@ -89,12 +94,25 @@ class SyclHandler(AugExpr): and id_regex.search(param.dtype.c_string()) is not None ) - id_param = list(filter(filter_id, kernel.scalar_parameters))[0] - - tree = make_sequence(*extras, SfgKernelCallNode(kernel)) + def sequencer(*args: SequencerArg): + id_param = [] + for arg in args: + if isinstance(arg, SfgKernelCallNode): + check_kernel(arg._kernel_handle) + id_param.append(list(filter(filter_id, arg._kernel_handle.scalar_parameters))[0]) + + if not all(item == id_param[0] for item in id_param): + raise ValueError( + "id_param should be the same for all kernels in parallel_for" + ) + tree = make_sequence(*args) + + kernel_lambda = SfgLambda(("=",), (id_param[0],), tree, None) + return SyclKernelInvoke( + self, SyclInvokeType.ParallelFor, range, kernel_lambda + ) - kernel_lambda = SfgLambda(("=",), (id_param,), tree, None) - return SyclKernelInvoke(self, SyclInvokeType.ParallelFor, range, kernel_lambda) + return sequencer class SyclGroup(AugExpr): diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py index c6cd011..e8618a5 100644 --- a/src/pystencilssfg/lang/cpp/sycl_accessor.py +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -1,5 +1,7 @@ +import math from ...lang import SrcField, IFieldExtraction from ...ir.source_components import SfgHeaderInclude +from typing import Sequence from pystencils import Field from pystencils.types import ( @@ -15,6 +17,7 @@ class SyclAccessor(SrcField): self, T: PsType, dimensions: int, + index_shape: Sequence[int], reference: bool = False, ): cpp_typestr = T.c_string() @@ -25,7 +28,11 @@ class SyclAccessor(SrcField): ) super().__init__(PsCustomType(typestring)) - self._dim = dimensions + self._spatial_dimensions = dimensions + self._index_dimensions = len(index_shape) + self._index_shape = index_shape + self._index_size = math.prod(index_shape) + self._total_dimensions_ = self._spatial_dimensions + self._index_dimensions @property def required_includes(self) -> set[SfgHeaderInclude]: @@ -42,7 +49,7 @@ class SyclAccessor(SrcField): ) def size(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._dim: + if coordinate > accessor._spatial_dimensions: return None else: return AugExpr.format( @@ -50,28 +57,36 @@ class SyclAccessor(SrcField): ) def stride(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._dim: + if coordinate > accessor._total_dimensions_: return None + elif coordinate >= accessor._spatial_dimensions - 1: + start = (coordinate - accessor._spatial_dimensions) + 1 + return AugExpr.format( + "{}", math.prod(accessor._index_shape[start:]) + ) else: - if coordinate == accessor._dim - 1: - return AugExpr.format("1") - else: - exprs = [] - args = [] - for d in range(coordinate + 1, accessor._dim): - args.extend([accessor, d]) - exprs.append("{}.get_range().get({})") - expr = " * ".join(exprs) - return AugExpr.format(expr, *args) + exprs = [] + args = [] + for d in range(coordinate + 1, accessor._spatial_dimensions): + args.extend([accessor, d]) + exprs.append("{}.get_range().get({})") + expr = " * ".join(exprs) + expr += " * {}" + return AugExpr.format(expr, *args, accessor._index_size) return Extraction() def sycl_accessor_ref(field: Field): """Creates a `sycl::accessor &` for a given pystencils field.""" + # Sycl accesors allow only at max 3 dimensions: + # So also mapping the index dimens to the sycl accesor we only can have 2D LBM stuff + # In principle it would be possible to map it to something like sycl::buffer<std::array<double, 19>, 3> + # but then would need to generate kernels that have sycl accessors as arguments return SyclAccessor( field.dtype, field.spatial_dimensions, + field.index_shape, reference=True, ).var(field.name) -- GitLab From 764f56e4147c28cf4f7a23a5dd29eddc854150e2 Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 28 Nov 2024 13:19:15 +0100 Subject: [PATCH 09/13] All dimensions of a field are now mapped to the accessor dimension, this allows only only 3 dims at maximum (spatial and index dim ) add some tests --- src/pystencilssfg/lang/cpp/sycl_accessor.py | 37 ++++++------------- tests/lang/test_sycl.py | 41 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 tests/lang/test_sycl.py diff --git a/src/pystencilssfg/lang/cpp/sycl_accessor.py b/src/pystencilssfg/lang/cpp/sycl_accessor.py index e8618a5..f704477 100644 --- a/src/pystencilssfg/lang/cpp/sycl_accessor.py +++ b/src/pystencilssfg/lang/cpp/sycl_accessor.py @@ -1,7 +1,5 @@ -import math from ...lang import SrcField, IFieldExtraction from ...ir.source_components import SfgHeaderInclude -from typing import Sequence from pystencils import Field from pystencils.types import ( @@ -17,22 +15,17 @@ class SyclAccessor(SrcField): self, T: PsType, dimensions: int, - index_shape: Sequence[int], reference: bool = False, ): cpp_typestr = T.c_string() - if dimensions not in [1, 2, 3]: + if 3 < dimensions: raise ValueError("sycl accessors can only have dims 1, 2 or 3") typestring = ( f"sycl::accessor< {cpp_typestr}, {dimensions} > {'&' if reference else ''}" ) super().__init__(PsCustomType(typestring)) - - self._spatial_dimensions = dimensions - self._index_dimensions = len(index_shape) - self._index_shape = index_shape - self._index_size = math.prod(index_shape) - self._total_dimensions_ = self._spatial_dimensions + self._index_dimensions + self._dim = dimensions + self._inner_stride = 1 @property def required_includes(self) -> set[SfgHeaderInclude]: @@ -49,7 +42,7 @@ class SyclAccessor(SrcField): ) def size(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._spatial_dimensions: + if coordinate > accessor._dim: return None else: return AugExpr.format( @@ -57,36 +50,30 @@ class SyclAccessor(SrcField): ) def stride(self, coordinate: int) -> AugExpr | None: - if coordinate > accessor._total_dimensions_: + if coordinate > accessor._dim: return None - elif coordinate >= accessor._spatial_dimensions - 1: - start = (coordinate - accessor._spatial_dimensions) + 1 - return AugExpr.format( - "{}", math.prod(accessor._index_shape[start:]) - ) + elif coordinate == accessor._dim - 1: + return AugExpr.format("{}", accessor._inner_stride) else: exprs = [] args = [] - for d in range(coordinate + 1, accessor._spatial_dimensions): + for d in range(coordinate + 1, accessor._dim): args.extend([accessor, d]) exprs.append("{}.get_range().get({})") expr = " * ".join(exprs) expr += " * {}" - return AugExpr.format(expr, *args, accessor._index_size) + return AugExpr.format(expr, *args, accessor._inner_stride) return Extraction() def sycl_accessor_ref(field: Field): """Creates a `sycl::accessor &` for a given pystencils field.""" - # Sycl accesors allow only at max 3 dimensions: - # So also mapping the index dimens to the sycl accesor we only can have 2D LBM stuff - # In principle it would be possible to map it to something like sycl::buffer<std::array<double, 19>, 3> - # but then would need to generate kernels that have sycl accessors as arguments + # Sycl Accessor do not expose information about strides, so the linearization is like here + # https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#_multi_dimensional_objects_and_linearization return SyclAccessor( field.dtype, - field.spatial_dimensions, - field.index_shape, + field.spatial_dimensions + field.index_dimensions, reference=True, ).var(field.name) diff --git a/tests/lang/test_sycl.py b/tests/lang/test_sycl.py new file mode 100644 index 0000000..88f958f --- /dev/null +++ b/tests/lang/test_sycl.py @@ -0,0 +1,41 @@ +import pytest + + +from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref +import pystencils as ps + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +@pytest.mark.parametrize("dim", [1, 2, 3]) +def test_spatial_field(data_type, dim): + f = ps.fields(f"f:{data_type}[{dim}D]") + ref = sycl_accessor_ref(f) + assert ( + f"sycl::accessor< {data_type}, {dim} > &" in ref.as_variable().name_and_type() + ) + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +def test_to_large_dim(data_type): + dim = 4 + f = ps.fields(f"f:{data_type}[{dim}D]") + with pytest.raises(ValueError): + sycl_accessor_ref(f) + + +@pytest.mark.parametrize("data_type", ["double", "float"]) +@pytest.mark.parametrize("spatial_dim", [1, 2, 3]) +@pytest.mark.parametrize("index_dims", [1, 2, 3]) +def test_index_field(data_type, spatial_dim, index_dims): + index_shape = ("19",) * index_dims + total_dims = spatial_dim + index_dims + f = ps.fields(f"f({', '.join(index_shape)}):{data_type}[{spatial_dim}D]") + if total_dims <= 3: + ref = sycl_accessor_ref(f) + assert ( + f"sycl::accessor< {data_type}, {total_dims} > &" + in ref.as_variable().name_and_type() + ) + else: + with pytest.raises(ValueError): + sycl_accessor_ref(f) -- GitLab From 77316bdfcba3e152e7c629fcd3defd0a3ea3f31d Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 28 Nov 2024 13:54:14 +0100 Subject: [PATCH 10/13] added a test for the parallel_for --- tests/extensions/test_sycl.py | 64 +++++++++++++++++++ .../{test_sycl.py => test_sycl_accessor.py} | 0 2 files changed, 64 insertions(+) create mode 100644 tests/extensions/test_sycl.py rename tests/lang/{test_sycl.py => test_sycl_accessor.py} (100%) diff --git a/tests/extensions/test_sycl.py b/tests/extensions/test_sycl.py new file mode 100644 index 0000000..db99278 --- /dev/null +++ b/tests/extensions/test_sycl.py @@ -0,0 +1,64 @@ +import pytest +from pystencilssfg import SourceFileGenerator +import pystencilssfg.extensions.sycl as sycl +import pystencils as ps +from pystencilssfg import SfgContext + + +def test_parallel_for_1_kernels(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g, h, i = ps.fields(f"f,g,h,i:{data_type}[{dim}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) + + +def test_parallel_for_2_kernels(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g, h, i = ps.fields(f"f,g,h,i:{data_type}[{dim}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) + + +def test_parallel_for_2_kernels_fail(): + sfg = sycl.SyclComposer(SfgContext()) + data_type = "double" + dim = 2 + f, g = ps.fields(f"f,g:{data_type}[{dim}D]") + h, i = ps.fields(f"h,i:{data_type}[{dim-1}D]") + assignement_1 = ps.Assignment(f.center(), g.center()) + assignement_2 = ps.Assignment(h.center(), i.center()) + + config = ps.CreateKernelConfig(target=ps.Target.SYCL) + kernel_1 = sfg.kernels.create(assignement_1, "kernel_1", config) + kernel_2 = sfg.kernels.create(assignement_2, "kernel_2", config) + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(dim, "range") + with pytest.raises(ValueError): + cgh.parallel_for(rang)( + sfg.call(kernel_1), + sfg.call(kernel_2), + ) diff --git a/tests/lang/test_sycl.py b/tests/lang/test_sycl_accessor.py similarity index 100% rename from tests/lang/test_sycl.py rename to tests/lang/test_sycl_accessor.py -- GitLab From 0ee586eff5cecfc74c996446514a50f355296dfc Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Tue, 3 Dec 2024 14:48:22 +0100 Subject: [PATCH 11/13] updated the integration test to new config api --- integration/test_sycl_buffer.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration/test_sycl_buffer.py b/integration/test_sycl_buffer.py index 7839b9f..a1e52eb 100644 --- a/integration/test_sycl_buffer.py +++ b/integration/test_sycl_buffer.py @@ -1,15 +1,14 @@ from pystencils import Target, CreateKernelConfig, no_jit from lbmpy import create_lb_update_rule, LBMOptimisation -from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgOutputMode +from pystencilssfg import SourceFileGenerator, SfgConfig, OutputMode from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref import pystencilssfg.extensions.sycl as sycl from itertools import chain -sfg_config = SfgConfiguration( +sfg_config = SfgConfig( output_directory="out/test_sycl_buffer", outer_namespace="gen_code", - impl_extension="ipp", - output_mode=SfgOutputMode.INLINE, + output_mode=OutputMode.INLINE, ) with SourceFileGenerator(sfg_config) as sfg: -- GitLab From f8ed8ad8d810be016badde83279265a61e187f3f Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 5 Dec 2024 08:45:37 +0100 Subject: [PATCH 12/13] Added a generator script to test the sycl accessors --- .../scripts/TestSyclBuffer.py | 35 +++++++++++++++++++ .../test_generator_scripts.py | 18 +++++++--- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/generator_scripts/scripts/TestSyclBuffer.py diff --git a/tests/generator_scripts/scripts/TestSyclBuffer.py b/tests/generator_scripts/scripts/TestSyclBuffer.py new file mode 100644 index 0000000..d041756 --- /dev/null +++ b/tests/generator_scripts/scripts/TestSyclBuffer.py @@ -0,0 +1,35 @@ +import pystencils as ps +import sympy as sp +from pystencilssfg import SourceFileGenerator +from pystencilssfg.lang.cpp.sycl_accessor import sycl_accessor_ref +import pystencilssfg.extensions.sycl as sycl + + +with SourceFileGenerator() as sfg: + sfg = sycl.SyclComposer(sfg) + + u_src, u_dst, f = ps.fields("u_src, u_dst, f : double[2D]", layout="fzyx") + h = sp.Symbol("h") + + jacobi_update = [ + ps.Assignment( + u_dst.center(), + (h**2 * f[0, 0] + u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) + / 4, + ) + ] + + kernel_config = ps.CreateKernelConfig(target=ps.Target.SYCL) + jacobi_kernel = sfg.kernels.create(jacobi_update, config=kernel_config) + + cgh = sfg.sycl_handler("handler") + rang = sfg.sycl_range(2, "range") + mappings = [ + sfg.map_field(u_src, sycl_accessor_ref(u_src)), + sfg.map_field(u_dst, sycl_accessor_ref(u_dst)), + sfg.map_field(f, sycl_accessor_ref(f)), + ] + + sfg.function("jacobiUpdate")( + cgh.parallel_for(rang)(*mappings, sfg.call(jacobi_kernel)), + ) diff --git a/tests/generator_scripts/test_generator_scripts.py b/tests/generator_scripts/test_generator_scripts.py index 16adba2..316e0e0 100644 --- a/tests/generator_scripts/test_generator_scripts.py +++ b/tests/generator_scripts/test_generator_scripts.py @@ -74,7 +74,7 @@ SCRIPTS = [ "--sfg-file-extensionss", ".c++,.h++", ), - should_fail=True + should_fail=True, ), ScriptInfo.make( "TestExtraCommandLineArgs", @@ -85,13 +85,19 @@ SCRIPTS = [ "--precision", "float32", "test1", - "test2" + "test2", ), ), ScriptInfo.make("Structural", ("hpp", "cpp")), ScriptInfo.make("SimpleJacobi", ("hpp", "cpp"), compilable_output="cpp"), ScriptInfo.make("SimpleClasses", ("hpp", "cpp")), ScriptInfo.make("Variables", ("hpp", "cpp"), compilable_output="cpp"), + ScriptInfo.make( + "TestSyclBuffer", + ("hpp", "cpp"), + compilable_output="cpp" if shutil.which("icpx") else None, + compile_cmd="icpx -fsycl -std=c++20" if shutil.which("icpx") else "", + ), ] @@ -113,13 +119,17 @@ def test_generator_script(script_info: ScriptInfo): shutil.rmtree(output_dir) os.makedirs(output_dir, exist_ok=True) - args = ["python", script_file, "--sfg-output-dir", output_dir] + list(script_info.args) + args = ["python", script_file, "--sfg-output-dir", output_dir] + list( + script_info.args + ) result = subprocess.run(args) if script_info.should_fail: if result.returncode == 0: - pytest.fail(f"Generator script {script_name} was supposed to fail, but didn't.") + pytest.fail( + f"Generator script {script_name} was supposed to fail, but didn't." + ) return if result.returncode != 0: -- GitLab From f02641fe3be5ccff79dcf01b4611aec11c06e40f Mon Sep 17 00:00:00 2001 From: Christoph Alt <christoph.alt@fau.de> Date: Thu, 5 Dec 2024 08:50:03 +0100 Subject: [PATCH 13/13] using get_dtype() to get the full typename --- tests/lang/test_sycl_accessor.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/lang/test_sycl_accessor.py b/tests/lang/test_sycl_accessor.py index 88f958f..d2af295 100644 --- a/tests/lang/test_sycl_accessor.py +++ b/tests/lang/test_sycl_accessor.py @@ -10,13 +10,11 @@ import pystencils as ps def test_spatial_field(data_type, dim): f = ps.fields(f"f:{data_type}[{dim}D]") ref = sycl_accessor_ref(f) - assert ( - f"sycl::accessor< {data_type}, {dim} > &" in ref.as_variable().name_and_type() - ) + assert f"sycl::accessor< {data_type}, {dim} > &" in str(ref.get_dtype()) @pytest.mark.parametrize("data_type", ["double", "float"]) -def test_to_large_dim(data_type): +def test_too_large_dim(data_type): dim = 4 f = ps.fields(f"f:{data_type}[{dim}D]") with pytest.raises(ValueError): @@ -32,10 +30,7 @@ def test_index_field(data_type, spatial_dim, index_dims): f = ps.fields(f"f({', '.join(index_shape)}):{data_type}[{spatial_dim}D]") if total_dims <= 3: ref = sycl_accessor_ref(f) - assert ( - f"sycl::accessor< {data_type}, {total_dims} > &" - in ref.as_variable().name_and_type() - ) + assert f"sycl::accessor< {data_type}, {total_dims} > &" in str(ref.get_dtype()) else: with pytest.raises(ValueError): sycl_accessor_ref(f) -- GitLab