From 9c3789f96c6024feacf37fe10f4b2dbc75360e73 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Wed, 1 Jul 2026 11:59:49 +0200 Subject: [PATCH 1/3] Added assign_to_container and assign_to_view --- include/xtensor/core/xassign.hpp | 111 +++++++++++++++++------------ include/xtensor/core/xsemantic.hpp | 28 +------- test/test_strided_assign.cpp | 2 - test/test_xstrided_view.cpp | 2 - test/test_xview.cpp | 2 - 5 files changed, 67 insertions(+), 78 deletions(-) diff --git a/include/xtensor/core/xassign.hpp b/include/xtensor/core/xassign.hpp index 174ddcdc0..add350f2c 100644 --- a/include/xtensor/core/xassign.hpp +++ b/include/xtensor/core/xassign.hpp @@ -37,12 +37,6 @@ namespace xt * Assign functions * ********************/ - template - void assign_data(xexpression& e1, const xexpression& e2, bool trivial); - - template - void assign_xexpression(xexpression& e1, const xexpression& e2); - template void computed_assign(xexpression& e1, const xexpression& e2); @@ -50,13 +44,10 @@ namespace xt void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); template - void assert_compatible_shape(const xexpression& e1, const xexpression& e2); - - template - void strided_assign(E1& e1, const E2& e2, std::false_type /*disable*/); + void assign_to_container(xexpression& e1, const xexpression& e2); template - void strided_assign(E1& e1, const E2& e2, std::true_type /*enable*/); + void assign_to_view(xexpression& e1, const xexpression& e2); /************************ * xexpression_assigner * @@ -81,9 +72,6 @@ namespace xt using base_type = xexpression_assigner_base; - template - static void assign_xexpression(E1& e1, const E2& e2); - template static void computed_assign(xexpression& e1, const xexpression& e2); @@ -91,7 +79,10 @@ namespace xt static void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); template - static void assert_compatible_shape(const xexpression& e1, const xexpression& e2); + static void assign_to_container(xexpression& e1, const xexpression& e2); + + template + static void assign_to_view(xexpression& e1, const xexpression& e2); private: @@ -100,6 +91,10 @@ namespace xt template static bool resize(E1& e1, const xfunction& e2); + + template + static void assert_compatible_shape(const xexpression& e1, const xexpression& e2); + }; /******************** @@ -205,10 +200,17 @@ namespace xt ***********************************/ template - inline void assign_data(xexpression& e1, const xexpression& e2, bool trivial) + inline void computed_assign(xexpression& e1, const xexpression& e2) + { + using tag = xexpression_tag_t; + xexpression_assigner::computed_assign(e1, e2); + } + + template + inline void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) { using tag = xexpression_tag_t; - xexpression_assigner::assign_data(e1, e2, trivial); + xexpression_assigner::scalar_computed_assign(e1, e2, std::forward(f)); } template @@ -221,29 +223,16 @@ namespace xt else { using tag = xexpression_tag_t; - xexpression_assigner::assign_xexpression(e1, e2); + xexpression_assigner::assign_to_container(e1, e2); } } - template - inline void computed_assign(xexpression& e1, const xexpression& e2) - { - using tag = xexpression_tag_t; - xexpression_assigner::computed_assign(e1, e2); - } - - template - inline void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) - { - using tag = xexpression_tag_t; - xexpression_assigner::scalar_computed_assign(e1, e2, std::forward(f)); - } template - inline void assert_compatible_shape(const xexpression& e1, const xexpression& e2) + inline void assign_to_view(xexpression& e1, const xexpression& e2) { using tag = xexpression_tag_t; - xexpression_assigner::assert_compatible_shape(e1, e2); + xexpression_assigner::assign_to_view(e1, e2); } /*************************************** @@ -479,12 +468,40 @@ namespace xt template template - inline void xexpression_assigner::assign_xexpression(E1& e1, const E2& e2) + inline void xexpression_assigner::assign_to_container(xexpression& e1, const xexpression& e2) { bool trivial_broadcast = resize(e1.derived_cast(), e2.derived_cast()); base_type::assign_data(e1, e2, trivial_broadcast); } + namespace detail + { + template + bool get_rhs_triviality(const F&) + { + return true; + } + + template + bool get_rhs_triviality(const xfunction& rhs) + { + using index_type = xindex_type_t::shape_type>; + using size_type = typename index_type::size_type; + size_type size = rhs.dimension(); + index_type shape = uninitialized_shape(size); + bool trivial_broadcast = rhs.broadcast_shape(shape, true); + return trivial_broadcast; + } + } + + template + template + inline void xexpression_assigner::assign_to_view(xexpression& e1, const xexpression& e2) + { + assert_compatible_shape(e1, e2); + base_type::assign_data(e1, e2, detail::get_rhs_triviality(e2.derived_cast())); + } + template template inline void xexpression_assigner::computed_assign(xexpression& e1, const xexpression& e2) @@ -536,19 +553,6 @@ namespace xt } } - template - template - inline void - xexpression_assigner::assert_compatible_shape(const xexpression& e1, const xexpression& e2) - { - const E1& de1 = e1.derived_cast(); - const E2& de2 = e2.derived_cast(); - if (!broadcastable(de2.shape(), de1.shape())) - { - throw_broadcast_error(de2.shape(), de1.shape()); - } - } - namespace detail { template @@ -606,6 +610,19 @@ namespace xt } } + template + template + inline void + xexpression_assigner::assert_compatible_shape(const xexpression& e1, const xexpression& e2) + { + const E1& de1 = e1.derived_cast(); + const E2& de2 = e2.derived_cast(); + if (!broadcastable(de2.shape(), de1.shape())) + { + throw_broadcast_error(de2.shape(), de1.shape()); + } + } + /*********************************** * stepper_assigner implementation * ***********************************/ diff --git a/include/xtensor/core/xsemantic.hpp b/include/xtensor/core/xsemantic.hpp index 05b3248c0..c9f59670a 100644 --- a/include/xtensor/core/xsemantic.hpp +++ b/include/xtensor/core/xsemantic.hpp @@ -658,7 +658,7 @@ namespace xt template inline auto xcontainer_semantic::assign_xexpression(const xexpression& e) -> derived_type& { - xt::assign_xexpression(*this, e); + xt::assign_to_container(*this, e); return this->derived_cast(); } @@ -701,32 +701,11 @@ namespace xt return this->derived_cast(); } - namespace detail - { - template - bool get_rhs_triviality(const F&) - { - return true; - } - - template - bool get_rhs_triviality(const xfunction& rhs) - { - using index_type = xindex_type_t::shape_type>; - using size_type = typename index_type::size_type; - size_type size = rhs.dimension(); - index_type shape = uninitialized_shape(size); - bool trivial_broadcast = rhs.broadcast_shape(shape, true); - return trivial_broadcast; - } - } - template template inline auto xview_semantic::assign_xexpression(const xexpression& e) -> derived_type& { - xt::assert_compatible_shape(*this, e); - xt::assign_data(*this, e, detail::get_rhs_triviality(e.derived_cast())); + xt::assign_to_view(*this, e); return this->derived_cast(); } @@ -734,8 +713,7 @@ namespace xt template inline auto xview_semantic::computed_assign(const xexpression& e) -> derived_type& { - xt::assert_compatible_shape(*this, e); - xt::assign_data(*this, e, detail::get_rhs_triviality(e.derived_cast())); + xt::assign_to_view(*this, e); return this->derived_cast(); } diff --git a/test/test_strided_assign.cpp b/test/test_strided_assign.cpp index a0131b27d..3f79a754f 100644 --- a/test/test_strided_assign.cpp +++ b/test/test_strided_assign.cpp @@ -27,12 +27,10 @@ namespace xt { auto check_linear_assign = [](auto a, auto b) { - assert_compatible_shape(a, b); return xassign_traits::linear_assign(a, b, true); }; auto check_strided_assign = [](auto a, auto b) { - assert_compatible_shape(a, b); #ifndef _WIN32 static_assert( xassign_traits::strided_assign(), diff --git a/test/test_xstrided_view.cpp b/test/test_xstrided_view.cpp index b916d2473..1d3906980 100644 --- a/test/test_xstrided_view.cpp +++ b/test/test_xstrided_view.cpp @@ -498,8 +498,6 @@ namespace xt EXPECT_FALSE(broadcastable(v.shape(), b.shape())); EXPECT_FALSE(broadcastable(b.shape(), v.shape())); - XT_EXPECT_THROW(assert_compatible_shape(b, v), broadcast_error); - XT_EXPECT_THROW(assert_compatible_shape(v, b), broadcast_error); XT_EXPECT_THROW(v = b, broadcast_error); XT_EXPECT_THROW(noalias(v) = b, broadcast_error); } diff --git a/test/test_xview.cpp b/test/test_xview.cpp index 8069bb016..5f00d244f 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -1005,8 +1005,6 @@ namespace xt EXPECT_FALSE(broadcastable(v.shape(), b.shape())); EXPECT_FALSE(broadcastable(b.shape(), v.shape())); - XT_EXPECT_THROW(assert_compatible_shape(b, v), broadcast_error); - XT_EXPECT_THROW(assert_compatible_shape(v, b), broadcast_error); XT_EXPECT_THROW(v = b, broadcast_error); XT_EXPECT_THROW(noalias(v) = b, broadcast_error); } From 041861b9fe5ffb38edb707a14e5e2dcd511ddbf3 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Fri, 3 Jul 2026 18:07:09 +0200 Subject: [PATCH 2/3] Implemented assign_policy and plugged it into semantics classes --- CMakeLists.txt | 2 +- benchmark/benchmark_adapter.cpp | 1 - benchmark/benchmark_assign.cpp | 1 - benchmark/benchmark_builder.cpp | 1 - benchmark/benchmark_lambda_expressions.cpp | 1 - benchmark/benchmark_math.cpp | 1 - benchmark/benchmark_random.cpp | 1 - benchmark/benchmark_view_access.cpp | 1 - benchmark/benchmark_view_adapt.cpp | 1 - benchmark/benchmark_view_assignment.cpp | 1 - benchmark/benchmark_views.cpp | 1 - docs/source/container.rst | 1 - include/xtensor/chunk/xchunked_assign.hpp | 26 +- include/xtensor/chunk/xchunked_view.hpp | 1 - include/xtensor/containers/xarray.hpp | 2 +- include/xtensor/containers/xfixed.hpp | 2 +- include/xtensor/containers/xtensor.hpp | 2 +- include/xtensor/core/xassign.hpp | 1134 +++------------- include/xtensor/core/xassign_proxy.hpp | 355 +++++ include/xtensor/core/xassigner.hpp | 1180 +++++++++++++++++ include/xtensor/core/xnoalias.hpp | 230 ---- include/xtensor/core/xsemantic.hpp | 189 +-- include/xtensor/misc/xfft.hpp | 1 - include/xtensor/optional/xoptional.hpp | 45 +- .../xtensor/optional/xoptional_assembly.hpp | 2 +- include/xtensor/views/xstrided_view.hpp | 27 +- include/xtensor/views/xview.hpp | 27 +- .../test_extended_broadcast_view.cppy | 1 - test/test_extended_broadcast_view.cpp | 1 - test/test_strided_assign.cpp | 1 - test/test_xassign.cpp | 1 - test/test_xchunked_array.cpp | 1 - test/test_xdynamic_view.cpp | 1 - test/test_xfixed.cpp | 1 - test/test_xfunctor_adaptor.cpp | 1 - test/test_xnoalias.cpp | 1 - test/test_xnorm.cpp | 1 - test/test_xscalar_semantic.cpp | 2 - test/test_xstrided_view.cpp | 1 - test/test_xview.cpp | 3 +- test/test_xview_semantic.cpp | 1 - 41 files changed, 1853 insertions(+), 1400 deletions(-) create mode 100644 include/xtensor/core/xassign_proxy.hpp create mode 100644 include/xtensor/core/xassigner.hpp delete mode 100644 include/xtensor/core/xnoalias.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 331fb48c2..0e5a1ae27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ set(XTENSOR_HEADERS ${XTENSOR_INCLUDE_DIR}/xtensor/containers/xtensor.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xaccessible.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xassign.hpp + ${XTENSOR_INCLUDE_DIR}/xtensor/core/xassign_proxy.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xeval.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xexpression.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xexpression_traits.hpp @@ -146,7 +147,6 @@ set(XTENSOR_HEADERS ${XTENSOR_INCLUDE_DIR}/xtensor/core/xlayout.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xmath.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xmultiindex_iterator.hpp - ${XTENSOR_INCLUDE_DIR}/xtensor/core/xnoalias.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xoperation.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xsemantic.hpp ${XTENSOR_INCLUDE_DIR}/xtensor/core/xshape.hpp diff --git a/benchmark/benchmark_adapter.cpp b/benchmark/benchmark_adapter.cpp index 3a76dcb7e..c8d73c56e 100644 --- a/benchmark/benchmark_adapter.cpp +++ b/benchmark/benchmark_adapter.cpp @@ -11,7 +11,6 @@ // #include "xtensor/core/core/xshape.hpp" #include "xtensor/containers/xadapt.hpp" #include "xtensor/containers/xstorage.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/utils/xutils.hpp" namespace xt diff --git a/benchmark/benchmark_assign.cpp b/benchmark/benchmark_assign.cpp index b9efb341e..8170a422b 100644 --- a/benchmark/benchmark_assign.cpp +++ b/benchmark/benchmark_assign.cpp @@ -13,7 +13,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" namespace xt { diff --git a/benchmark/benchmark_builder.cpp b/benchmark/benchmark_builder.cpp index e25de39cb..61d946c60 100644 --- a/benchmark/benchmark_builder.cpp +++ b/benchmark/benchmark_builder.cpp @@ -10,7 +10,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" namespace xt { diff --git a/benchmark/benchmark_lambda_expressions.cpp b/benchmark/benchmark_lambda_expressions.cpp index 6c6c40a49..f2a9af903 100644 --- a/benchmark/benchmark_lambda_expressions.cpp +++ b/benchmark/benchmark_lambda_expressions.cpp @@ -11,7 +11,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" #include "xtensor/core/xmath.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" namespace xt diff --git a/benchmark/benchmark_math.cpp b/benchmark/benchmark_math.cpp index c365551ea..3fa9a175d 100644 --- a/benchmark/benchmark_math.cpp +++ b/benchmark/benchmark_math.cpp @@ -15,7 +15,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" // For how many sizes should math functions be tested? #define MATH_RANGE 64, 64 diff --git a/benchmark/benchmark_random.cpp b/benchmark/benchmark_random.cpp index e5279988d..39f722d27 100644 --- a/benchmark/benchmark_random.cpp +++ b/benchmark/benchmark_random.cpp @@ -13,7 +13,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xrandom.hpp" namespace xt diff --git a/benchmark/benchmark_view_access.cpp b/benchmark/benchmark_view_access.cpp index 78569955e..d40f6c821 100644 --- a/benchmark/benchmark_view_access.cpp +++ b/benchmark/benchmark_view_access.cpp @@ -11,7 +11,6 @@ // #include "xtensor/core/core/xshape.hpp" #include "xtensor/containers/xadapt.hpp" #include "xtensor/containers/xstorage.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/utils/xutils.hpp" diff --git a/benchmark/benchmark_view_adapt.cpp b/benchmark/benchmark_view_adapt.cpp index 2523cb725..c7cc4d1d9 100644 --- a/benchmark/benchmark_view_adapt.cpp +++ b/benchmark/benchmark_view_adapt.cpp @@ -14,7 +14,6 @@ #include "xtensor/containers/xadapt.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/views/xview.hpp" diff --git a/benchmark/benchmark_view_assignment.cpp b/benchmark/benchmark_view_assignment.cpp index 3d5ae314b..ce488a3f0 100644 --- a/benchmark/benchmark_view_assignment.cpp +++ b/benchmark/benchmark_view_assignment.cpp @@ -11,7 +11,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xrandom.hpp" namespace xt diff --git a/benchmark/benchmark_views.cpp b/benchmark/benchmark_views.cpp index f2c34d510..0d9976f23 100644 --- a/benchmark/benchmark_views.cpp +++ b/benchmark/benchmark_views.cpp @@ -14,7 +14,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/core/xstrides.hpp" #include "xtensor/misc/xmanipulation.hpp" #include "xtensor/views/xstrided_view.hpp" diff --git a/docs/source/container.rst b/docs/source/container.rst index 74367b7a8..6a4edbd46 100644 --- a/docs/source/container.rst +++ b/docs/source/container.rst @@ -184,7 +184,6 @@ A mechanism is provided to forcibly prevent usage of a temporary variable: .. code:: #include - #include // a, b, and c are xt::xarrays previously initialized xt::noalias(b) = a + c; diff --git a/include/xtensor/chunk/xchunked_assign.hpp b/include/xtensor/chunk/xchunked_assign.hpp index 334e824b9..fde8a71c9 100644 --- a/include/xtensor/chunk/xchunked_assign.hpp +++ b/include/xtensor/chunk/xchunked_assign.hpp @@ -10,7 +10,7 @@ #ifndef XTENSOR_CHUNKED_ASSIGN_HPP #define XTENSOR_CHUNKED_ASSIGN_HPP -#include "../core/xnoalias.hpp" +#include "../core/xassign_proxy.hpp" #include "../views/xstrided_view.hpp" namespace xt @@ -44,14 +44,14 @@ namespace xt using derived_type = D; using temporary_type = typename base_type::temporary_type; - template - derived_type& assign_xexpression(const xexpression& e); + template > + derived_type& assign_xexpression(const xexpression& e, P p = P{}); - template - derived_type& computed_assign(const xexpression& e); + template > + derived_type& computed_assign(const xexpression& e, P p = P{}); - template - derived_type& scalar_computed_assign(const E& e, F&& f); + template > + derived_type& scalar_computed_assign(const E& e, F&& f, P p = P{}); protected: @@ -201,8 +201,8 @@ namespace xt } template - template - inline auto xchunked_semantic::assign_xexpression(const xexpression& e) -> derived_type& + template + inline auto xchunked_semantic::assign_xexpression(const xexpression& e, P) -> derived_type& { auto& d = this->derived_cast(); const auto& chunk_shape = d.chunk_shape(); @@ -225,8 +225,8 @@ namespace xt } template - template - inline auto xchunked_semantic::computed_assign(const xexpression& e) -> derived_type& + template + inline auto xchunked_semantic::computed_assign(const xexpression& e, P) -> derived_type& { D& d = this->derived_cast(); if (e.derived_cast().dimension() > d.dimension() || e.derived_cast().shape() > d.shape()) @@ -240,8 +240,8 @@ namespace xt } template - template - inline auto xchunked_semantic::scalar_computed_assign(const E& e, F&& f) -> derived_type& + template + inline auto xchunked_semantic::scalar_computed_assign(const E& e, F&& f, P) -> derived_type& { for (auto& c : this->derived_cast().chunks()) { diff --git a/include/xtensor/chunk/xchunked_view.hpp b/include/xtensor/chunk/xchunked_view.hpp index 6c9d0a3f2..e65de159a 100644 --- a/include/xtensor/chunk/xchunked_view.hpp +++ b/include/xtensor/chunk/xchunked_view.hpp @@ -14,7 +14,6 @@ #include "../chunk/xchunked_array.hpp" #include "../containers/xstorage.hpp" -#include "../core/xnoalias.hpp" #include "../views/xstrided_view.hpp" namespace xt diff --git a/include/xtensor/containers/xarray.hpp b/include/xtensor/containers/xarray.hpp index 7f27bd296..3019de46c 100644 --- a/include/xtensor/containers/xarray.hpp +++ b/include/xtensor/containers/xarray.hpp @@ -506,7 +506,7 @@ namespace xt { detail::resize_data_container(m_storage, std::size_t(1)); } - semantic_base::assign(e); + semantic_base::assign(e, noalias); } /** diff --git a/include/xtensor/containers/xfixed.hpp b/include/xtensor/containers/xfixed.hpp index e1b02867d..0bb938011 100644 --- a/include/xtensor/containers/xfixed.hpp +++ b/include/xtensor/containers/xfixed.hpp @@ -628,7 +628,7 @@ namespace xt template inline xfixed_container::xfixed_container(const xexpression& e) { - semantic_base::assign(e); + semantic_base::assign(e, noalias); } /** diff --git a/include/xtensor/containers/xtensor.hpp b/include/xtensor/containers/xtensor.hpp index db8d92385..2b3ed44f7 100644 --- a/include/xtensor/containers/xtensor.hpp +++ b/include/xtensor/containers/xtensor.hpp @@ -569,7 +569,7 @@ namespace xt { detail::resize_data_container(m_storage, std::size_t(1)); } - semantic_base::assign(e); + semantic_base::assign(e, noalias); } /** diff --git a/include/xtensor/core/xassign.hpp b/include/xtensor/core/xassign.hpp index add350f2c..7c3fa7a58 100644 --- a/include/xtensor/core/xassign.hpp +++ b/include/xtensor/core/xassign.hpp @@ -18,6 +18,8 @@ #include #include +#include "../core/xassign_proxy.hpp" +#include "../core/xassigner.hpp" #include "../core/xexpression.hpp" #include "../core/xfunction.hpp" #include "../core/xiterator.hpp" @@ -26,10 +28,6 @@ #include "../core/xtensor_forward.hpp" #include "../utils/xutils.hpp" -#if defined(XTENSOR_USE_TBB) -#include -#endif - namespace xt { @@ -37,17 +35,17 @@ namespace xt * Assign functions * ********************/ - template - void computed_assign(xexpression& e1, const xexpression& e2); + template + void computed_assign(xexpression& e1, const xexpression& e2, P p); - template - void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); + template + void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f, P p); - template - void assign_to_container(xexpression& e1, const xexpression& e2); + template + void assign_to_container(xexpression& e1, const xexpression& e2, P p); - template - void assign_to_view(xexpression& e1, const xexpression& e2); + template + void assign_to_view(xexpression& e1, const xexpression& e2, P p); /************************ * xexpression_assigner * @@ -61,8 +59,8 @@ namespace xt { public: - template - static void assign_data(xexpression& e1, const xexpression& e2, bool trivial); + template + static void assign_data(xexpression& e1, const xexpression& e2, bool trivial, P p); }; template @@ -72,149 +70,56 @@ namespace xt using base_type = xexpression_assigner_base; - template - static void computed_assign(xexpression& e1, const xexpression& e2); - - template - static void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f); - - template - static void assign_to_container(xexpression& e1, const xexpression& e2); - - template - static void assign_to_view(xexpression& e1, const xexpression& e2); - - private: - - template - static bool resize(E1& e1, const E2& e2); - - template - static bool resize(E1& e1, const xfunction& e2); - - template - static void assert_compatible_shape(const xexpression& e1, const xexpression& e2); - - }; - - /******************** - * stepper_assigner * - ********************/ - - template - class stepper_assigner - { - public: - - using lhs_iterator = typename E1::stepper; - using rhs_iterator = typename E2::const_stepper; - using shape_type = typename E1::shape_type; - using index_type = xindex_type_t; - using size_type = typename lhs_iterator::size_type; - using difference_type = typename lhs_iterator::difference_type; - - stepper_assigner(E1& e1, const E2& e2); - - void run(); - - void step(size_type i); - void step(size_type i, size_type n); - void reset(size_type i); - - void to_end(layout_type); - - private: - - E1& m_e1; - - lhs_iterator m_lhs; - rhs_iterator m_rhs; - - index_type m_index; - }; - - /******************* - * linear_assigner * - *******************/ - - template - class linear_assigner - { - public: + template + static void computed_assign(xexpression& e1, const xexpression& e2, P p); - template - static void run(E1& e1, const E2& e2); - }; + template + static void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f, P p); - template <> - class linear_assigner - { - public: + template + static void assign_to_container(xexpression& e1, const xexpression& e2, P p); - template - static void run(E1& e1, const E2& e2); + template + static void assign_to_view(xexpression& e1, const xexpression& e2, P p); private: - template - static void run_impl(E1& e1, const E2& e2, std::true_type); - - template - static void run_impl(E1& e1, const E2& e2, std::false_type); - }; + template + static typename E::shape_type get_nobroadcast_shape(const E& e); - /************************* - * strided_loop_assigner * - *************************/ + template + static typename xfunction::shape_type get_nobroadcast_shape(const xfunction& f); - namespace strided_assign_detail - { - struct loop_sizes_t - { - bool can_do_strided_assign; - bool is_row_major; - std::size_t inner_loop_size; - std::size_t outer_loop_size; - std::size_t cut; - std::size_t dimension; - }; - } + template + static bool resize(E1& e1, const E2& e2, P); - template - class strided_loop_assigner - { - public: + template + static bool resize(E1& e1, const xfunction& e2, P); - using loop_sizes_t = strided_assign_detail::loop_sizes_t; - // is_row_major, inner_loop_size, outer_loop_size, cut - template - static void run(E1& e1, const E2& e2, const loop_sizes_t& loop_sizes); - template - static loop_sizes_t get_loop_sizes(E1& e1, const E2& e2); - template - static void run(E1& e1, const E2& e2); + template + static void assert_compatible_shape(const xexpression& e1, const xexpression& e2, P); }; /*********************************** * Assign functions implementation * ***********************************/ - template - inline void computed_assign(xexpression& e1, const xexpression& e2) + template + inline void computed_assign(xexpression& e1, const xexpression& e2, P p) { using tag = xexpression_tag_t; - xexpression_assigner::computed_assign(e1, e2); + xexpression_assigner::computed_assign(e1, e2, p); } - template - inline void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) + template + inline void scalar_computed_assign(xexpression& e1, const E2& e2, F&& f, P p) { using tag = xexpression_tag_t; - xexpression_assigner::scalar_computed_assign(e1, e2, std::forward(f)); + xexpression_assigner::scalar_computed_assign(e1, e2, std::forward(f), p); } - template - inline void assign_xexpression(xexpression& e1, const xexpression& e2) + template + inline void assign_to_container(xexpression& e1, const xexpression& e2, P p) { if constexpr (assignable_to_expression) { @@ -223,16 +128,15 @@ namespace xt else { using tag = xexpression_tag_t; - xexpression_assigner::assign_to_container(e1, e2); + xexpression_assigner::assign_to_container(e1, e2, p); } } - - template - inline void assign_to_view(xexpression& e1, const xexpression& e2) + template + inline void assign_to_view(xexpression& e1, const xexpression& e2, P p) { using tag = xexpression_tag_t; - xexpression_assigner::assign_to_view(e1, e2); + xexpression_assigner::assign_to_view(e1, e2, p); } /*************************************** @@ -308,33 +212,6 @@ namespace xt { static constexpr bool value = std::conjunction>...>::value; }; - - /** - * Considering the assignment LHS = RHS, if the requested value type used for - * loading simd from RHS is not complex while LHS value_type is complex, - * the assignment fails. The reason is that SIMD batches of complex values cannot - * be implicitly instantiated from batches of scalar values. - * Making the constructor implicit does not fix the issue since in the end, - * the assignment is done with vec.store(buffer) where vec is a batch of scalars - * and buffer an array of complex. SIMD batches of scalars do not provide overloads - * of store that accept buffer of complex values and that SHOULD NOT CHANGE. - * Load and store overloads must accept SCALAR BUFFERS ONLY. - * Therefore, the solution is to explicitly force the instantiation of complex - * batches in the assignment mechanism. A common situation that triggers this - * issue is: - * xt::xarray rhs = { 1, 2, 3 }; - * xt::xarray> lhs = rhs; - */ - template - struct conditional_promote_to_complex - { - static constexpr bool cond = xtl::is_gen_complex::value && !xtl::is_gen_complex::value; - // Alternative: use std::complex or xcomplex depending on T1 - using type = std::conditional_t; - }; - - template - using conditional_promote_to_complex_t = typename conditional_promote_to_complex::type; } template @@ -381,8 +258,8 @@ namespace xt static constexpr bool simd_interface() { - return has_simd_interface() - && has_simd_interface(); + using rvt = requested_value_type_t; + return has_simd_interface() && has_simd_interface(); } public: @@ -419,17 +296,14 @@ namespace xt { return simd_assign() && detail::linear_dynamic_layout(e1, e2); } - - using e2_requested_value_type = std:: - conditional_t::value, typename E2::bool_load_type, e2_value_type>; - using requested_value_type = detail::conditional_promote_to_complex_t; }; - template + template inline void xexpression_assigner_base::assign_data( xexpression& e1, const xexpression& e2, - bool trivial + bool trivial, + P ) { E1& de1 = e1.derived_cast(); @@ -440,6 +314,9 @@ namespace xt constexpr bool simd_assign = traits::simd_assign(); constexpr bool simd_linear_assign = traits::simd_linear_assign(); constexpr bool simd_strided_assign = traits::simd_strided_assign(); + + parallel_assigner_t assigner; + if (linear_assign) { if (simd_linear_assign || traits::simd_linear_assign(de1, de2)) @@ -449,62 +326,73 @@ namespace xt // in compilation error for expressions that do not provide a SIMD interface. // simd_assign is true if simd_linear_assign() or simd_linear_assign(de1, de2) // is true. - linear_assigner::run(de1, de2); + assigner.template linear_assign(de1, de2); } else { - linear_assigner::run(de1, de2); + assigner.template linear_assign(de1, de2); } } - else if (simd_strided_assign) - { - strided_loop_assigner::run(de1, de2); - } else { - stepper_assigner(de1, de2).run(); + if constexpr (simd_strided_assign) + { + assigner.strided_loop_assign(de1, de2); + } + else + { + assigner.template stepper_assign(de1, de2); + } } } template - template - inline void xexpression_assigner::assign_to_container(xexpression& e1, const xexpression& e2) + template + inline void + xexpression_assigner::assign_to_container(xexpression& e1, const xexpression& e2, P p) { - bool trivial_broadcast = resize(e1.derived_cast(), e2.derived_cast()); - base_type::assign_data(e1, e2, trivial_broadcast); + bool trivial_broadcast = resize(e1.derived_cast(), e2.derived_cast(), p); + base_type::assign_data(e1, e2, trivial_broadcast, p); } namespace detail { - template - bool get_rhs_triviality(const F&) + template + bool get_rhs_triviality(const F&, P) { return true; } - template - bool get_rhs_triviality(const xfunction& rhs) + template + bool get_rhs_triviality(const xfunction& rhs, P) { - using index_type = xindex_type_t::shape_type>; - using size_type = typename index_type::size_type; - size_type size = rhs.dimension(); - index_type shape = uninitialized_shape(size); - bool trivial_broadcast = rhs.broadcast_shape(shape, true); - return trivial_broadcast; + if constexpr (has_broadcasting

) + { + using index_type = xindex_type_t::shape_type>; + using size_type = typename index_type::size_type; + size_type size = rhs.dimension(); + index_type shape = uninitialized_shape(size); + bool trivial_broadcast = rhs.broadcast_shape(shape, true); + return trivial_broadcast; + } + else + { + return true; + } } } template - template - inline void xexpression_assigner::assign_to_view(xexpression& e1, const xexpression& e2) + template + inline void xexpression_assigner::assign_to_view(xexpression& e1, const xexpression& e2, P p) { - assert_compatible_shape(e1, e2); - base_type::assign_data(e1, e2, detail::get_rhs_triviality(e2.derived_cast())); + assert_compatible_shape(e1, e2, p); + base_type::assign_data(e1, e2, detail::get_rhs_triviality(e2.derived_cast(), p), p); } template - template - inline void xexpression_assigner::computed_assign(xexpression& e1, const xexpression& e2) + template + inline void xexpression_assigner::computed_assign(xexpression& e1, const xexpression& e2, P p) { using shape_type = typename E1::shape_type; using comperator_type = std::greater; @@ -514,34 +402,41 @@ namespace xt E1& de1 = e1.derived_cast(); const E2& de2 = e2.derived_cast(); - size_type dim2 = de2.dimension(); - shape_type shape = uninitialized_shape(dim2); + if constexpr (has_broadcasting

) + { + size_type dim2 = de2.dimension(); + shape_type shape = uninitialized_shape(dim2); - bool trivial_broadcast = de2.broadcast_shape(shape, true); + bool trivial_broadcast = de2.broadcast_shape(shape, true); - auto&& de1_shape = de1.shape(); - if (dim2 > de1.dimension() - || std::lexicographical_compare( - shape.begin(), - shape.end(), - de1_shape.begin(), - de1_shape.end(), - comperator_type() - )) - { - typename E1::temporary_type tmp(shape); - base_type::assign_data(tmp, e2, trivial_broadcast); - de1.assign_temporary(std::move(tmp)); + auto&& de1_shape = de1.shape(); + if (dim2 > de1.dimension() + || std::lexicographical_compare( + shape.begin(), + shape.end(), + de1_shape.begin(), + de1_shape.end(), + comperator_type() + )) + { + typename E1::temporary_type tmp(shape); + base_type::assign_data(tmp, e2, trivial_broadcast, p); + de1.assign_temporary(std::move(tmp)); + } + else + { + base_type::assign_data(e1, e2, trivial_broadcast, p); + } } else { - base_type::assign_data(e1, e2, trivial_broadcast); + base_type::assign_data(e1, e2, true, p); } } template - template - inline void xexpression_assigner::scalar_computed_assign(xexpression& e1, const E2& e2, F&& f) + template + inline void xexpression_assigner::scalar_computed_assign(xexpression& e1, const E2& e2, F&& f, P) { E1& d = e1.derived_cast(); using size_type = typename E1::size_type; @@ -572,8 +467,8 @@ namespace xt } template - template - inline bool xexpression_assigner::resize(E1& e1, const E2& e2) + template + inline bool xexpression_assigner::resize(E1& e1, const E2& e2, P) { // If our RHS is not a xfunction, we know that the RHS is at least potentially trivial // We check the strides of the RHS in detail::is_trivial_broadcast to see if they match up! @@ -583,8 +478,24 @@ namespace xt } template - template - inline bool xexpression_assigner::resize(E1& e1, const xfunction& e2) + template + inline auto xexpression_assigner::get_nobroadcast_shape(const E& e) -> typename E::shape_type + { + return e.shape(); + } + + template + template + inline auto xexpression_assigner::get_nobroadcast_shape(const xfunction& f) -> + typename xfunction::shape_type + { + auto& first_arg = std::get<0>(f.arguments()); + return get_nobroadcast_shape(first_arg); + } + + template + template + inline bool xexpression_assigner::resize(E1& e1, const xfunction& e2, P) { if constexpr (detail::is_fixed::shape_type>::value) { @@ -600,779 +511,48 @@ namespace xt } else { - using index_type = xindex_type_t; - using size_type = typename E1::size_type; - size_type size = e2.dimension(); - index_type shape = uninitialized_shape(size); - bool trivial_broadcast = e2.broadcast_shape(shape, true); - e1.resize(std::move(shape)); - return trivial_broadcast; - } - } - - template - template - inline void - xexpression_assigner::assert_compatible_shape(const xexpression& e1, const xexpression& e2) - { - const E1& de1 = e1.derived_cast(); - const E2& de2 = e2.derived_cast(); - if (!broadcastable(de2.shape(), de1.shape())) - { - throw_broadcast_error(de2.shape(), de1.shape()); - } - } - - /*********************************** - * stepper_assigner implementation * - ***********************************/ - - template - struct is_narrowing_conversion - { - using argument_type = std::decay_t; - using result_type = std::decay_t; - - static const bool value = xtl::is_arithmetic::value - && (sizeof(result_type) < sizeof(argument_type) - || (xtl::is_integral::value - && std::is_floating_point::value)); - }; - - template - struct has_sign_conversion - { - using argument_type = std::decay_t; - using result_type = std::decay_t; - - static const bool value = xtl::is_signed::value != xtl::is_signed::value; - }; - - template - struct has_assign_conversion - { - using argument_type = std::decay_t; - using result_type = std::decay_t; - - static const bool value = is_narrowing_conversion::value - || has_sign_conversion::value; - }; - - template - inline stepper_assigner::stepper_assigner(E1& e1, const E2& e2) - : m_e1(e1) - , m_lhs(e1.stepper_begin(e1.shape())) - , m_rhs(e2.stepper_begin(e1.shape())) - , m_index(xtl::make_sequence(e1.shape().size(), size_type(0))) - { - } - - template - inline void stepper_assigner::run() - { - using tmp_size_type = typename E1::size_type; - using argument_type = std::decay_t; - using result_type = std::decay_t; - constexpr bool needs_cast = has_assign_conversion::value; - - tmp_size_type s = m_e1.size(); - for (tmp_size_type i = 0; i < s; ++i) - { - *m_lhs = conditional_cast(*m_rhs); - stepper_tools::increment_stepper(*this, m_index, m_e1.shape()); - } - } - - template - inline void stepper_assigner::step(size_type i) - { - m_lhs.step(i); - m_rhs.step(i); - } - - template - inline void stepper_assigner::step(size_type i, size_type n) - { - m_lhs.step(i, n); - m_rhs.step(i, n); - } - - template - inline void stepper_assigner::reset(size_type i) - { - m_lhs.reset(i); - m_rhs.reset(i); - } - - template - inline void stepper_assigner::to_end(layout_type l) - { - m_lhs.to_end(l); - m_rhs.to_end(l); - } - - /********************************** - * linear_assigner implementation * - **********************************/ - - template - template - inline void linear_assigner::run(E1& e1, const E2& e2) - { - using lhs_align_mode = xt_simd::container_alignment_t; - constexpr bool is_aligned = std::is_same::value; - using rhs_align_mode = std::conditional_t; - using e1_value_type = typename E1::value_type; - using e2_value_type = typename E2::value_type; - using value_type = typename xassign_traits::requested_value_type; - using simd_type = xt_simd::simd_type; - using size_type = typename E1::size_type; - size_type size = e1.size(); - constexpr size_type simd_size = simd_type::size; - constexpr bool needs_cast = has_assign_conversion::value; - - size_type align_begin = is_aligned ? 0 : xt_simd::get_alignment_offset(e1.data(), size, simd_size); - size_type align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); - - for (size_type i = 0; i < align_begin; ++i) - { - e1.data_element(i) = conditional_cast(e2.data_element(i)); - } - -#if defined(XTENSOR_USE_TBB) - if (size >= XTENSOR_TBB_THRESHOLD) - { - tbb::static_partitioner ap; - tbb::parallel_for( - align_begin, - align_end, - simd_size, - [&e1, &e2](size_t i) - { - e1.template store_simd( - i, - e2.template load_simd(i) - ); - }, - ap - ); - } - else - { - for (size_type i = align_begin; i < align_end; i += simd_size) - { - e1.template store_simd(i, e2.template load_simd(i)); - } - } -#elif defined(XTENSOR_USE_OPENMP) - if (size >= size_type(XTENSOR_OPENMP_TRESHOLD)) - { -#pragma omp parallel for default(none) shared(align_begin, align_end, e1, e2) -#ifndef _WIN32 - for (size_type i = align_begin; i < align_end; i += simd_size) - { - e1.template store_simd(i, e2.template load_simd(i)); - } -#else - for (auto i = static_cast(align_begin); i < static_cast(align_end); - i += static_cast(simd_size)) - { - size_type ui = static_cast(i); - e1.template store_simd(ui, e2.template load_simd(ui)); - } -#endif - } - else - { - for (size_type i = align_begin; i < align_end; i += simd_size) - { - e1.template store_simd(i, e2.template load_simd(i)); - } - } -#else - for (size_type i = align_begin; i < align_end; i += simd_size) - { - e1.template store_simd(i, e2.template load_simd(i)); - } -#endif - for (size_type i = align_end; i < size; ++i) - { - e1.data_element(i) = conditional_cast(e2.data_element(i)); - } - } - - template - inline void linear_assigner::run(E1& e1, const E2& e2) - { - using is_convertible = std:: - is_convertible::value_type, typename std::decay_t::value_type>; - // If the types are not compatible, this function is still instantiated but never called. - // To avoid compilation problems in effectively unused code trivial_assigner_run_impl is - // empty in this case. - run_impl(e1, e2, is_convertible()); - } - - template - inline void linear_assigner::run_impl(E1& e1, const E2& e2, std::true_type /*is_convertible*/) - { - using value_type = typename E1::value_type; - using size_type = typename E1::size_type; - auto src = linear_begin(e2); - auto dst = linear_begin(e1); - size_type n = e1.size(); -#if defined(XTENSOR_USE_TBB) - tbb::static_partitioner sp; - tbb::parallel_for( - std::ptrdiff_t(0), - static_cast(n), - [&](std::ptrdiff_t i) - { - *(dst + i) = static_cast(*(src + i)); - }, - sp - ); -#elif defined(XTENSOR_USE_OPENMP) - if (n >= XTENSOR_OPENMP_TRESHOLD) - { -#pragma omp parallel for default(none) shared(src, dst, n) - for (std::ptrdiff_t i = std::ptrdiff_t(0); i < static_cast(n); i++) - { - *(dst + i) = static_cast(*(src + i)); - } - } - else - { - for (; n > size_type(0); --n) - { - *dst = static_cast(*src); - ++src; - ++dst; - } - } -#else - for (; n > size_type(0); --n) - { - *dst = static_cast(*src); - ++src; - ++dst; - } -#endif - } - - template - inline void linear_assigner::run_impl(E1&, const E2&, std::false_type /*is_convertible*/) - { - XTENSOR_PRECONDITION(false, "Internal error: linear_assigner called with unrelated types."); - } - - /**************************************** - * strided_loop_assigner implementation * - ****************************************/ - - namespace strided_assign_detail - { - template - struct idx_tools; - - template <> - struct idx_tools - { - template - static void next_idx(T& outer_index, T& outer_shape) - { - auto i = outer_index.size(); - for (; i > 0; --i) - { - if (outer_index[i - 1] + 1 >= outer_shape[i - 1]) - { - outer_index[i - 1] = 0; - } - else - { - outer_index[i - 1]++; - break; - } - } - } - - template - static void nth_idx(size_t n, T& outer_index, const T& outer_shape) - { - dynamic_shape stride_sizes; - xt::resize_container(stride_sizes, outer_shape.size()); - // compute strides - using size_type = typename T::size_type; - for (size_type i = outer_shape.size(); i > 0; i--) - { - stride_sizes[i - 1] = (i == outer_shape.size()) ? 1 : stride_sizes[i] * outer_shape[i]; - } - - // compute index - for (size_type i = 0; i < outer_shape.size(); i++) - { - auto d_idx = n / stride_sizes[i]; - outer_index[i] = d_idx; - n -= d_idx * stride_sizes[i]; - } - } - }; - - template <> - struct idx_tools - { - template - static void next_idx(T& outer_index, T& outer_shape) - { - using size_type = typename T::size_type; - size_type i = 0; - auto sz = outer_index.size(); - for (; i < sz; ++i) - { - if (outer_index[i] + 1 >= outer_shape[i]) - { - outer_index[i] = 0; - } - else - { - outer_index[i]++; - break; - } - } - } - - template - static void nth_idx(size_t n, T& outer_index, const T& outer_shape) - { - dynamic_shape stride_sizes; - xt::resize_container(stride_sizes, outer_shape.size()); - - using size_type = typename T::size_type; - - // compute required strides - for (size_type i = 0; i < outer_shape.size(); i++) - { - stride_sizes[i] = (i == 0) ? 1 : stride_sizes[i - 1] * outer_shape[i - 1]; - } - - // compute index - for (size_type i = outer_shape.size(); i > 0;) - { - i--; - auto d_idx = n / stride_sizes[i]; - outer_index[i] = d_idx; - n -= d_idx * stride_sizes[i]; - } - } - }; - - template - struct check_strides_functor - { - using strides_type = S; - - check_strides_functor(const S& strides) - : m_cut(L == layout_type::row_major ? 0 : strides.size()) - , m_strides(strides) - { - } - - template - std::enable_if_t operator()(const T& el) + if constexpr (has_broadcasting

) { - // All dimenions less than var have differing strides - auto var = check_strides_overlap::get(m_strides, el.strides()); - if (var > m_cut) - { - m_cut = var; - } - return m_cut; - } - - template - std::enable_if_t operator()(const T& el) - { - auto var = check_strides_overlap::get(m_strides, el.strides()); - // All dimensions >= var have differing strides - if (var < m_cut) - { - m_cut = var; - } - return m_cut; - } - - template - std::size_t operator()(const xt::xscalar& /*el*/) - { - return m_cut; - } - - template - std::size_t operator()(const xt::xfunction& xf) - { - xt::for_each(*this, xf.arguments()); - return m_cut; - } - - private: - - std::size_t m_cut; - const strides_type& m_strides; - }; - - template || !possible, bool> = true> - loop_sizes_t get_loop_sizes(const E1& e1, const E2&) - { - return {false, true, 1, e1.size(), e1.dimension(), e1.dimension()}; - } - - template && possible, bool> = true> - loop_sizes_t get_loop_sizes(const E1& e1, const E2& e2) - { - using shape_value_type = typename E1::shape_type::value_type; - bool is_row_major = true; - - // Try to find a row-major scheme first, where the outer loop is on the first N = `cut` - // dimensions, and the inner loop is - is_row_major = true; - auto is_zero = [](auto i) - { - return i == 0; - }; - auto&& strides = e1.strides(); - auto it_bwd = std::find_if_not(strides.rbegin(), strides.rend(), is_zero); - bool de1_row_contiguous = it_bwd != strides.rend() && *it_bwd == 1; - auto it_fwd = std::find_if_not(strides.begin(), strides.end(), is_zero); - bool de1_col_contiguous = it_fwd != strides.end() && *it_fwd == 1; - if (de1_row_contiguous) - { - is_row_major = true; - } - else if (de1_col_contiguous) - { - is_row_major = false; + using index_type = xindex_type_t; + using size_type = typename E1::size_type; + size_type size = e2.dimension(); + index_type shape = uninitialized_shape(size); + bool trivial_broadcast = e2.broadcast_shape(shape, true); + e1.resize(std::move(shape)); + return trivial_broadcast; } else { - // No strided loop possible. - return {false, true, 1, e1.size(), e1.dimension(), e1.dimension()}; - } - - // Cut is the number of dimensions in the outer loop - std::size_t cut = 0; - - if (is_row_major) - { - auto csf = check_strides_functor(e1.strides()); - cut = csf(e2); - // This makes that only one dimension will be treated in the inner loop. - if (cut < e1.strides().size() - 1) - { - // Only make the inner loop go over one dimension by default for now - cut = e1.strides().size() - 1; - } + using shape_type = typename xfunction::shape_type; + shape_type shape = get_nobroadcast_shape(e2); + e1.resize(std::move(shape)); + return true; } - else if (!is_row_major) - { - auto csf = check_strides_functor(e1.strides() - ); - cut = csf(e2); - if (cut > 1) - { - // Only make the inner loop go over one dimension by default for now - cut = 1; - } - } // can't reach here because this would have already triggered the fallback - - std::size_t outer_loop_size = static_cast(std::accumulate( - e1.shape().begin(), - e1.shape().begin() + static_cast(cut), - shape_value_type(1), - std::multiplies{} - )); - std::size_t inner_loop_size = static_cast(std::accumulate( - e1.shape().begin() + static_cast(cut), - e1.shape().end(), - shape_value_type(1), - std::multiplies{} - )); - - if (!is_row_major) - { - std::swap(outer_loop_size, inner_loop_size); - } - - return {inner_loop_size > 1, is_row_major, inner_loop_size, outer_loop_size, cut, e1.dimension()}; } } - template - template - inline strided_assign_detail::loop_sizes_t strided_loop_assigner::get_loop_sizes(E1& e1, const E2& e2) - { - return strided_assign_detail::get_loop_sizes(e1, e2); - } - -#define strided_parallel_assign - - template - template - inline void strided_loop_assigner::run(E1& e1, const E2& e2, const loop_sizes_t& loop_sizes) + template + template + inline void + xexpression_assigner::assert_compatible_shape(const xexpression& e1, const xexpression& e2, P) { - bool is_row_major = loop_sizes.is_row_major; - std::size_t inner_loop_size = loop_sizes.inner_loop_size; - std::size_t outer_loop_size = loop_sizes.outer_loop_size; - std::size_t cut = loop_sizes.cut; - - - // TODO can we get rid of this and use `shape_type`? - dynamic_shape idx, max_shape; - - if (is_row_major) - { - xt::resize_container(idx, cut); - max_shape.assign(e1.shape().begin(), e1.shape().begin() + static_cast(cut)); - } - else - { - xt::resize_container(idx, e1.shape().size() - cut); - max_shape.assign(e1.shape().begin() + static_cast(cut), e1.shape().end()); - } - - // add this when we have std::array index! - // std::fill(idx.begin(), idx.end(), 0); - using e1_value_type = typename E1::value_type; - using e2_value_type = typename E2::value_type; - constexpr bool needs_cast = has_assign_conversion::value; - using value_type = typename xassign_traits::requested_value_type; - using simd_type = std::conditional_t< - std::is_same::value, - xt_simd::simd_bool_type, - xt_simd::simd_type>; - - std::size_t simd_size = inner_loop_size / simd_type::size; - std::size_t simd_rest = inner_loop_size % simd_type::size; - - auto fct_stepper = e2.stepper_begin(e1.shape()); - auto res_stepper = e1.stepper_begin(e1.shape()); - - // TODO in 1D case this is ambiguous -- could be RM or CM. - // Use default layout to make decision - std::size_t step_dim = 0; - if (!is_row_major) // row major case - { - step_dim = cut; - } -#if defined(XTENSOR_USE_OPENMP) && defined(strided_parallel_assign) - if (outer_loop_size >= XTENSOR_OPENMP_TRESHOLD / inner_loop_size) + const E1& de1 = e1.derived_cast(); + const E2& de2 = e2.derived_cast(); + if constexpr (has_broadcasting

) { - std::size_t first_step = true; -#pragma omp parallel for schedule(static) firstprivate(first_step, fct_stepper, res_stepper, idx) - for (std::size_t ox = 0; ox < outer_loop_size; ++ox) + if (!broadcastable(de2.shape(), de1.shape())) { - if (first_step) - { - is_row_major - ? strided_assign_detail::idx_tools::nth_idx(ox, idx, max_shape) - : strided_assign_detail::idx_tools::nth_idx(ox, idx, max_shape); - - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - res_stepper.step(i + step_dim, idx[i]); - } - first_step = false; - } - - for (std::size_t i = 0; i < simd_size; ++i) - { - res_stepper.store_simd(fct_stepper.template step_simd()); - } - for (std::size_t i = 0; i < simd_rest; ++i) - { - *(res_stepper) = conditional_cast(*(fct_stepper)); - res_stepper.step_leading(); - fct_stepper.step_leading(); - } - - // next unaligned index - is_row_major - ? strided_assign_detail::idx_tools::next_idx(idx, max_shape) - : strided_assign_detail::idx_tools::next_idx(idx, max_shape); - - fct_stepper.to_begin(); - - // need to step E1 as well if not contigous assign (e.g. view) - if (!E1::contiguous_layout) - { - res_stepper.to_begin(); - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - res_stepper.step(i + step_dim, idx[i]); - } - } - else - { - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - } - } + throw_broadcast_error(de2.shape(), de1.shape()); } } else { -#elif defined(strided_parallel_assign) && defined(XTENSOR_USE_TBB) - if (outer_loop_size > XTENSOR_TBB_THRESHOLD / inner_loop_size) - { - tbb::static_partitioner sp; - tbb::parallel_for( - tbb::blocked_range(0ul, outer_loop_size), - [&e1, &e2, is_row_major, step_dim, simd_size, simd_rest, &max_shape, &idx_ = idx]( - const tbb::blocked_range& r - ) - { - auto idx = idx_; - auto fct_stepper = e2.stepper_begin(e1.shape()); - auto res_stepper = e1.stepper_begin(e1.shape()); - std::size_t first_step = true; - // #pragma omp parallel for schedule(static) firstprivate(first_step, fct_stepper, - // res_stepper, idx) - for (std::size_t ox = r.begin(); ox < r.end(); ++ox) - { - if (first_step) - { - is_row_major - ? strided_assign_detail::idx_tools::nth_idx(ox, idx, max_shape) - : strided_assign_detail::idx_tools::nth_idx( - ox, - idx, - max_shape - ); - - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - res_stepper.step(i + step_dim, idx[i]); - } - first_step = false; - } - - for (std::size_t i = 0; i < simd_size; ++i) - { - res_stepper.store_simd(fct_stepper.template step_simd()); - } - for (std::size_t i = 0; i < simd_rest; ++i) - { - *(res_stepper) = conditional_cast(*(fct_stepper)); - res_stepper.step_leading(); - fct_stepper.step_leading(); - } - - // next unaligned index - is_row_major - ? strided_assign_detail::idx_tools::next_idx(idx, max_shape) - : strided_assign_detail::idx_tools::next_idx(idx, max_shape); - - fct_stepper.to_begin(); - - // need to step E1 as well if not contigous assign (e.g. view) - if (!E1::contiguous_layout) - { - res_stepper.to_begin(); - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - res_stepper.step(i + step_dim, idx[i]); - } - } - else - { - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - } - } - } - }, - sp - ); - } - else - { - -#endif - for (std::size_t ox = 0; ox < outer_loop_size; ++ox) + if (!de1.shape() == de2.shape()) { - for (std::size_t i = 0; i < simd_size; ++i) - { - res_stepper.store_simd(fct_stepper.template step_simd()); - } - for (std::size_t i = 0; i < simd_rest; ++i) - { - *(res_stepper) = conditional_cast(*(fct_stepper)); - res_stepper.step_leading(); - fct_stepper.step_leading(); - } - - is_row_major - ? strided_assign_detail::idx_tools::next_idx(idx, max_shape) - : strided_assign_detail::idx_tools::next_idx(idx, max_shape); - - fct_stepper.to_begin(); - - // need to step E1 as well if not contigous assign (e.g. view) - if (!E1::contiguous_layout) - { - res_stepper.to_begin(); - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - res_stepper.step(i + step_dim, idx[i]); - } - } - else - { - for (std::size_t i = 0; i < idx.size(); ++i) - { - fct_stepper.step(i + step_dim, idx[i]); - } - } + throw_broadcast_error(de2.shape(), de1.shape()); } -#if (defined(XTENSOR_USE_OPENMP) || defined(XTENSOR_USE_TBB)) && defined(strided_parallel_assign) - } -#endif - } - - template <> - template - inline void strided_loop_assigner::run(E1& e1, const E2& e2) - { - strided_assign_detail::loop_sizes_t loop_sizes = strided_loop_assigner::get_loop_sizes(e1, e2); - if (loop_sizes.can_do_strided_assign) - { - run(e1, e2, loop_sizes); - } - else - { - // trigger the fallback assigner - stepper_assigner(e1, e2).run(); } } - - template <> - template - inline void strided_loop_assigner::run(E1& /*e1*/, const E2& /*e2*/, const loop_sizes_t&) - { - } - - template <> - template - inline void strided_loop_assigner::run(E1& e1, const E2& e2) - { - // trigger the fallback assigner - stepper_assigner(e1, e2).run(); - } } #endif diff --git a/include/xtensor/core/xassign_proxy.hpp b/include/xtensor/core/xassign_proxy.hpp new file mode 100644 index 000000000..628ef16b2 --- /dev/null +++ b/include/xtensor/core/xassign_proxy.hpp @@ -0,0 +1,355 @@ +/*************************************************************************** + * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * + * Copyright (c) QuantStack * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ****************************************************************************/ + +#pragma once + +#include + +#include "../core/xexpression.hpp" + +namespace xt +{ + /***************** + * assign_policy * + *****************/ + + struct alias_tag + { + }; + + struct noalias_tag + { + }; + + struct broadcast_tag + { + }; + + struct nobroadcast_tag + { + }; + + struct default_parallel_tag + { + }; + + struct noparallel_tag + { + }; + + struct tbb_tag + { + }; + + struct mpi_tag + { + }; + + template + concept aliasing_policy = std::same_as or std::same_as; + + template + concept broadcasting_policy = std::same_as or std::same_as; + + template + concept parallel_policy = std::same_as or std::same_as + or std::same_as or std::same_as; + + template + constexpr auto operator|(A1, A2) noexcept; + + template + constexpr auto operator|(B1, B2) noexcept; + + template + constexpr auto operator|(P1, P2) noexcept; + + template + struct assign_policy + { + using aliasing = A; + using broadcasting = B; + using parallelism = P; + + template + auto operator()(AT&& arr) const noexcept; + }; + + template + constexpr auto operator|(assign_policy, assign_policy) noexcept; + + inline constexpr assign_policy<> default_assign{}; + inline constexpr assign_policy noalias{}; + inline constexpr assign_policy nobroadcast{}; + inline constexpr assign_policy noparallel{}; + inline constexpr assign_policy tbb{}; + inline constexpr assign_policy mpi{}; + + template + concept has_aliasing = std::same_as; + + template + concept has_broadcasting = std::same_as; + + /**************** + * assign_proxy * + ****************/ + + template + class xassign_proxy + { + public: + + xassign_proxy(A a) noexcept; + + template + disable_xexpression operator=(const E&); + + template + disable_xexpression operator+=(const E&); + + template + disable_xexpression operator-=(const E&); + + template + disable_xexpression operator*=(const E&); + + template + disable_xexpression operator/=(const E&); + + template + disable_xexpression operator%=(const E&); + + template + disable_xexpression operator&=(const E&); + + template + disable_xexpression operator|=(const E&); + + template + disable_xexpression operator^=(const E&); + + template + A operator=(const xexpression&); + + template + A operator+=(const xexpression&); + + template + A operator-=(const xexpression& e); + + template + A operator*=(const xexpression& e); + + template + A operator/=(const xexpression& e); + + template + A operator%=(const xexpression& e); + + template + A operator&=(const xexpression&); + + template + A operator|=(const xexpression&); + + template + A operator^=(const xexpression&); + + private: + + A m_array; + }; + + /******************************** + * assign_policy implementation * + ********************************/ + + template + constexpr auto operator|(A1, A2) noexcept + { + if constexpr (std::same_as) + { + return A2{}; + } + return A1{}; + } + + template + constexpr auto operator|(B1, B2) noexcept + { + if constexpr (std::same_as) + { + return B2{}; + } + return B1{}; + } + + template + constexpr auto operator|(P1, P2) noexcept + { + if constexpr (std::same_as) + { + return P2{}; + } + return P1{}; + } + + template + template + inline auto assign_policy::operator()(AT&& arr) const noexcept + { + return xassign_proxy, assign_policy>{std::forward(arr)}; + } + + template + constexpr auto operator|(assign_policy, assign_policy) noexcept + { + return assign_policy< + decltype(std::declval() | std::declval()), + decltype(std::declval() | std::declval()), + decltype(std::declval() | std::declval())>{}; + } + + /******************************* + * assign_proxy implementation * + *******************************/ + + template + inline xassign_proxy::xassign_proxy(A a) noexcept + : m_array(std::forward(a)) + { + } + + template + template + inline auto xassign_proxy::operator=(const E& e) -> disable_xexpression + { + return m_array.assign(xscalar(e), P{}); + } + + template + template + inline auto xassign_proxy::operator+=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::plus<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator-=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::minus<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator*=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::multiplies<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator/=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::divides<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator%=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::modulus<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator&=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::bit_and<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator|=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::bit_or<>{}, P{}); + } + + template + template + inline auto xassign_proxy::operator^=(const E& e) -> disable_xexpression + { + return m_array.scalar_computed_assign(e, std::bit_xor<>{}, P{}); + } + + template + template + A xassign_proxy::operator=(const xexpression& e) + { + return m_array.assign(e, P{}); + } + + template + template + A xassign_proxy::operator+=(const xexpression& e) + { + return m_array.plus_assign(e, P{}); + } + + template + template + A xassign_proxy::operator-=(const xexpression& e) + { + return m_array.minus_assign(e, P{}); + } + + template + template + A xassign_proxy::operator*=(const xexpression& e) + { + return m_array.multiplies_assign(e, P{}); + } + + template + template + A xassign_proxy::operator/=(const xexpression& e) + { + return m_array.divides_assign(e, P{}); + } + + template + template + A xassign_proxy::operator%=(const xexpression& e) + { + return m_array.modulus_assign(e, P{}); + } + + template + template + A xassign_proxy::operator&=(const xexpression& e) + { + return m_array.bit_and_assign(e, P{}); + } + + template + template + A xassign_proxy::operator|=(const xexpression& e) + { + return m_array.bit_or_assign(e, P{}); + } + + template + template + A xassign_proxy::operator^=(const xexpression& e) + { + return m_array.bit_xor_assign(e, P{}); + } +} diff --git a/include/xtensor/core/xassigner.hpp b/include/xtensor/core/xassigner.hpp new file mode 100644 index 000000000..bd3dcefcc --- /dev/null +++ b/include/xtensor/core/xassigner.hpp @@ -0,0 +1,1180 @@ +/*************************************************************************** + * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * + * Copyright (c) QuantStack * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ****************************************************************************/ + +#include "../core/xassign_proxy.hpp" +#include "../core/xfunction.hpp" + +#if defined(XTENSOR_USE_TBB) +#include +#endif + +#if defined(XTENSOR_USE_OPENMP) +#include +#endif + +namespace xt +{ + /************* + * Assigners * + *************/ + + template + class noparallel_assigner + { + public: + + template + void linear_assign(E1& e1, const E2& e2) const; + + template + void stepper_assign(E1& e1, const E2& e2) const; + + void strided_loop_assign(E1& e1, const E2& e2) const; + + private: + + void linear_assign_no_simd(E1& e1, const E2& e2) const; + }; + +#if defined(XTENSOR_USE_TBB) + template + class tbb_assigner + { + public: + + template + void linear_assign(E1& e1, const E2& e2) const; + + template + void stepper_assign(E1& e1, const E2& e2) const; + + void strided_loop_assign(E1& e1, const E2& e2) const; + + private: + + void linear_assign_no_simd(E1& e1, const E2& e2) const; + }; +#endif + +#if defined(XTENSOR_USE_OPENMP) + template + class openmp_assigner + { + public: + + template + void linear_assign(E1& e1, const E2& e2) const; + + template + void stepper_assign(E1& e1, const E2& e2) const; + + void strided_loop_assign(E1& e1, const E2& e2) const; + + private: + + void linear_assign_no_simd(E1& e1, const E2& e2) const; + }; +#endif + + template + class waterfall_assigner + { + public: + + template + void linear_assign(E1& e1, const E2& e2) const; + + template + void stepper_assign(E1& e1, const E2& e2) const; + + void strided_loop_assign(E1& e1, const E2& e2) const; + }; + + /**************************** + * tags - assigners mapping * + ****************************/ + + template + struct parallel_assigner; + + template + struct parallel_assigner + { + using type = noparallel_assigner; + }; + +#if defined(XTENSOR_USE_TBB) + template + struct parallel_assigner + { + using type = tbb_assigner; + }; +#endif + +#if defined(XTENSOR_USE_OPENMP) + template + struct parallel_assigner + { + using type = openmp_assigner; + }; +#endif + + template + struct parallel_assigner + { + using type = waterfall_assigner; + }; + + template + using parallel_assigner_t = typename parallel_assigner::type; + + /*********** + * Helpers * + ***********/ + + template + struct is_narrowing_conversion + { + using argument_type = std::decay_t; + using result_type = std::decay_t; + + static const bool value = xtl::is_arithmetic::value + && (sizeof(result_type) < sizeof(argument_type) + || (xtl::is_integral::value + && std::is_floating_point::value)); + }; + + template + struct has_sign_conversion + { + using argument_type = std::decay_t; + using result_type = std::decay_t; + + static const bool value = xtl::is_signed::value != xtl::is_signed::value; + }; + + template + struct has_assign_conversion + { + using argument_type = std::decay_t; + using result_type = std::decay_t; + + static const bool value = is_narrowing_conversion::value + || has_sign_conversion::value; + }; + + namespace detail + { + /** + * Considering the assignment LHS = RHS, if the requested value type used for + * loading simd from RHS is not complex while LHS value_type is complex, + * the assignment fails. The reason is that SIMD batches of complex values cannot + * be implicitly instantiated from batches of scalar values. + * Making the constructor implicit does not fix the issue since in the end, + * the assignment is done with vec.store(buffer) where vec is a batch of scalars + * and buffer an array of complex. SIMD batches of scalars do not provide overloads + * of store that accept buffer of complex values and that SHOULD NOT CHANGE. + * Load and store overloads must accept SCALAR BUFFERS ONLY. + * Therefore, the solution is to explicitly force the instantiation of complex + * batches in the assignment mechanism. A common situation that triggers this + * issue is: + * xt::xarray rhs = { 1, 2, 3 }; + * xt::xarray> lhs = rhs; + */ + template + struct conditional_promote_to_complex + { + static constexpr bool cond = xtl::is_gen_complex::value && !xtl::is_gen_complex::value; + // Alternative: use std::complex or xcomplex depending on T1 + using type = std::conditional_t; + }; + + template + using conditional_promote_to_complex_t = typename conditional_promote_to_complex::type; + } + + template + struct requested_value_type + { + using e1_value_type = typename E1::value_type; + using e2_value_type = typename E2::value_type; + + using e2_requested_value_type = std:: + conditional_t, typename E2::bool_load_type, e2_value_type>; + using type = detail::conditional_promote_to_complex_t; + }; + + template + using requested_value_type_t = typename requested_value_type::type; + + template + class linear_assigner + { + public: + + using lhs_align_mode = xt_simd::container_alignment_t; + static constexpr bool is_aligned = std::is_same::value; + using rhs_align_mode = std::conditional_t; + using e1_value_type = typename E1::value_type; + using e2_value_type = typename E2::value_type; + using value_type = requested_value_type_t; + using simd_type = xt_simd::simd_type; + using size_type = typename E1::size_type; + static constexpr size_type simd_size = xt_simd::simd_traits::size; + static constexpr bool needs_cast = has_assign_conversion::value; + static constexpr bool is_convertible = std::is_convertible::value; + + size_type alignment_begin(const E1& e1, size_type size) const; + size_type alignment_end(size_type align_begin, size_type size) const; + + void assign_elements(E1& e1, const E2& e2, size_type begin, size_type end) const; + void assign_simd(E1& e1, const E2& e2, size_type begin, size_type end) const; + + // Assigns elements [begin, end) through linear iterators, without SIMD + void assign_elements_no_simd(E1& e1, const E2& e2, size_type begin, size_type end) const; + }; + + template + class stepper_assigner + { + public: + + using lhs_iterator = typename E1::stepper; + using rhs_iterator = typename E2::const_stepper; + using shape_type = typename E1::shape_type; + using index_type = xindex_type_t; + using size_type = typename lhs_iterator::size_type; + using difference_type = typename lhs_iterator::difference_type; + + stepper_assigner(E1& e1, const E2& e2); + + void run(); + + void step(size_type i); + void step(size_type i, size_type n); + void reset(size_type i); + + void to_end(layout_type); + + private: + + E1& m_e1; + + lhs_iterator m_lhs; + rhs_iterator m_rhs; + + index_type m_index; + }; + + namespace strided_assign_detail + { + struct loop_sizes_t + { + bool can_do_strided_assign; + bool is_row_major; + std::size_t inner_loop_size; + std::size_t outer_loop_size; + std::size_t cut; + std::size_t dimension; + }; + } + + template + class strided_loop_assigner + { + public: + + using size_type = std::size_t; + using index_type = dynamic_shape; + using loop_sizes_t = strided_assign_detail::loop_sizes_t; + using lhs_stepper = typename E1::stepper; + using rhs_stepper = typename E2::const_stepper; + using e1_value_type = typename E1::value_type; + using e2_value_type = typename E2::value_type; + using value_type = requested_value_type_t; + using simd_type = std::conditional_t< + std::is_same::value, + xt_simd::simd_bool_type, + xt_simd::simd_type>; + static constexpr bool needs_cast = has_assign_conversion::value; + + // Holds the per-thread state (steppers and outer index) required + // to assign a sequence of consecutive outer iterations. + class cursor + { + public: + + explicit cursor(const strided_loop_assigner& assigner); + + // Assigns the inner loop corresponding to the outer iteration ox, + // and moves to the next outer iteration. The first call positions + // the steppers on ox, subsequent calls must be made with ox + 1. + void assign_row(size_type ox); + + private: + + void step_to_index(); + + const strided_loop_assigner* p_assigner; + lhs_stepper m_lhs; + rhs_stepper m_rhs; + index_type m_index; + bool m_positioned; + }; + + strided_loop_assigner(E1& e1, const E2& e2); + + bool can_do_strided_assign() const noexcept; + size_type inner_loop_size() const noexcept; + size_type outer_loop_size() const noexcept; + + cursor make_cursor() const; + + // Assigns the outer iterations [begin, end) + void run(size_type begin, size_type end) const; + + private: + + void nth_idx(size_type n, index_type& index) const; + void next_idx(index_type& index) const; + + E1& m_e1; + const E2& m_e2; + loop_sizes_t m_loop_sizes; + index_type m_max_shape; + size_type m_step_dim; + size_type m_simd_size; + size_type m_simd_rest; + }; + + /************************************** + * noparallel_assigner implementation * + **************************************/ + + template + template + void noparallel_assigner::linear_assign(E1& e1, const E2& e2) const + { + if constexpr (!simd_assign) + { + linear_assign_no_simd(e1, e2); + } + else + { + using assigner_t = linear_assigner; + using size_type = typename assigner_t::size_type; + + assigner_t helper; + size_type size = e1.size(); + auto align_begin = helper.alignment_begin(e1, size); + auto align_end = helper.alignment_end(align_begin, size); + + helper.assign_elements(e1, e2, 0u, align_begin); + helper.assign_simd(e1, e2, align_begin, align_end); + helper.assign_elements(e1, e2, align_end, size); + } + } + + template + template + void noparallel_assigner::stepper_assign(E1& e1, const E2& e2) const + { + stepper_assigner helper(e1, e2); + helper.run(); + } + + template + void noparallel_assigner::strided_loop_assign(E1& e1, const E2& e2) const + { + strided_loop_assigner helper(e1, e2); + if (!helper.can_do_strided_assign()) + { + stepper_assign(e1, e2); + return; + } + + helper.run(0u, helper.outer_loop_size()); + } + + template + void noparallel_assigner::linear_assign_no_simd(E1& e1, const E2& e2) const + { + linear_assigner{}.assign_elements_no_simd(e1, e2, 0u, e1.size()); + } + + /******************************* + * tbb_assigner implementation * + *******************************/ + +#if defined(XTENSOR_USE_TBB) + template + template + void tbb_assigner::linear_assign(E1& e1, const E2& e2) const + { + if constexpr (!simd_assign) + { + linear_assign_no_simd(e1, e2); + } + else + { + using assigner_t = linear_assigner; + using size_type = typename assigner_t::size_type; + constexpr size_type simd_size = assigner_t::simd_size; + + assigner_t helper; + size_type size = e1.size(); + auto align_begin = helper.alignment_begin(e1, size); + auto align_end = helper.alignment_end(align_begin, size); + + helper.assign_elements(e1, e2, 0u, align_begin); + if (size >= XTENSOR_TBB_THRESHOLD) + { + tbb::static_partitioner ap; + tbb::parallel_for( + align_begin, + align_end, + simd_size, + [&helper, &e1, &e2](size_type i) + { + helper.assign_simd(e1, e2, i, i + simd_size); + }, + ap + ); + } + else + { + helper.assign_simd(e1, e2, align_begin, align_end); + } + helper.assign_elements(e1, e2, align_end, size); + } + } + + template + template + void tbb_assigner::stepper_assign(E1& e1, const E2& e2) const + { + stepper_assigner helper(e1, e2); + helper.run(); + } + + template + void tbb_assigner::strided_loop_assign(E1& e1, const E2& e2) const + { + using assigner_t = strided_loop_assigner; + using size_type = typename assigner_t::size_type; + + assigner_t helper(e1, e2); + if (!helper.can_do_strided_assign()) + { + stepper_assign(e1, e2); + return; + } + + size_type outer_loop_size = helper.outer_loop_size(); + if (outer_loop_size > XTENSOR_TBB_THRESHOLD / helper.inner_loop_size()) + { + tbb::static_partitioner sp; + tbb::parallel_for( + tbb::blocked_range(size_type(0), outer_loop_size), + [&helper](const tbb::blocked_range& r) + { + helper.run(r.begin(), r.end()); + }, + sp + ); + } + else + { + helper.run(0u, outer_loop_size); + } + } + + template + void tbb_assigner::linear_assign_no_simd(E1& e1, const E2& e2) const + { + using assigner_t = linear_assigner; + using size_type = typename assigner_t::size_type; + + assigner_t helper; + tbb::static_partitioner sp; + tbb::parallel_for( + tbb::blocked_range(size_type(0), e1.size()), + [&helper, &e1, &e2](const tbb::blocked_range& r) + { + helper.assign_elements_no_simd(e1, e2, r.begin(), r.end()); + }, + sp + ); + } +#endif + + /********************************** + * openmp_assigner implementation * + **********************************/ + +#if defined(XTENSOR_USE_OPENMP) + template + template + void openmp_assigner::linear_assign(E1& e1, const E2& e2) const + { + if constexpr (!simd_assign) + { + linear_assign_no_simd(e1, e2); + } + else + { + using assigner_t = linear_assigner; + using size_type = typename assigner_t::size_type; + constexpr size_type simd_size = assigner_t::simd_size; + + assigner_t helper; + size_type size = e1.size(); + auto align_begin = helper.alignment_begin(e1, size); + auto align_end = helper.alignment_end(align_begin, size); + + helper.assign_elements(e1, e2, 0u, align_begin); + if (size >= size_type(XTENSOR_OPENMP_TRESHOLD)) + { + // Signed loop index for MSVC, which only supports OpenMP 2.0 +#pragma omp parallel for default(none) shared(helper, align_begin, align_end, e1, e2) + for (auto i = static_cast(align_begin); + i < static_cast(align_end); + i += static_cast(simd_size)) + { + size_type ui = static_cast(i); + helper.assign_simd(e1, e2, ui, ui + simd_size); + } + } + else + { + helper.assign_simd(e1, e2, align_begin, align_end); + } + helper.assign_elements(e1, e2, align_end, size); + } + } + + template + template + void openmp_assigner::stepper_assign(E1& e1, const E2& e2) const + { + stepper_assigner helper(e1, e2); + helper.run(); + } + + template + void openmp_assigner::strided_loop_assign(E1& e1, const E2& e2) const + { + using assigner_t = strided_loop_assigner; + using size_type = typename assigner_t::size_type; + + assigner_t helper(e1, e2); + if (!helper.can_do_strided_assign()) + { + stepper_assign(e1, e2); + return; + } + + size_type outer_loop_size = helper.outer_loop_size(); + if (outer_loop_size >= XTENSOR_OPENMP_TRESHOLD / helper.inner_loop_size()) + { + // Each thread gets its own copy of the cursor, which positions itself + // on the first outer iteration of the chunk assigned to the thread. + auto cursor = helper.make_cursor(); +#pragma omp parallel for schedule(static) firstprivate(cursor) + for (std::ptrdiff_t ox = 0; ox < static_cast(outer_loop_size); ++ox) + { + cursor.assign_row(static_cast(ox)); + } + } + else + { + helper.run(0u, outer_loop_size); + } + } + + template + void openmp_assigner::linear_assign_no_simd(E1& e1, const E2& e2) const + { + using assigner_t = linear_assigner; + using size_type = typename assigner_t::size_type; + + assigner_t helper; + size_type n = e1.size(); + + if (n >= size_type(XTENSOR_OPENMP_TRESHOLD)) + { + // Each thread assigns a contiguous chunk, as schedule(static) would do +#pragma omp parallel default(none) shared(helper, e1, e2, n) + { + size_type nb_threads = static_cast(omp_get_num_threads()); + size_type thread_id = static_cast(omp_get_thread_num()); + size_type chunk = n / nb_threads; + size_type rest = n % nb_threads; + size_type begin = thread_id * chunk + std::min(thread_id, rest); + size_type end = begin + chunk + (thread_id < rest ? 1u : 0u); + helper.assign_elements_no_simd(e1, e2, begin, end); + } + } + else + { + helper.assign_elements_no_simd(e1, e2, 0u, n); + } + } +#endif + + /************************************* + * waterfall_assigner implementation * + *************************************/ + + template + template + void waterfall_assigner::linear_assign(E1& e1, const E2& e2) const + { +#if defined(XTENSOR_USE_TBB) + tbb_assigner{}.template linear_assign(e1, e2); +#elif defined(XTENSOR_USE_OPENMP) + openmp_assigner{}.template linear_assign(e1, e2); +#else + noparallel_assigner{}.template linear_assign(e1, e2); +#endif + } + + template + template + void waterfall_assigner::stepper_assign(E1& e1, const E2& e2) const + { + stepper_assigner helper(e1, e2); + helper.run(); + } + + template + void waterfall_assigner::strided_loop_assign(E1& e1, const E2& e2) const + { +#if defined(XTENSOR_USE_TBB) + tbb_assigner{}.strided_loop_assign(e1, e2); +#elif defined(XTENSOR_USE_OPENMP) + openmp_assigner{}.strided_loop_assign(e1, e2); +#else + noparallel_assigner{}.strided_loop_assign(e1, e2); +#endif + } + + /********************************** + * linear_assigner implementation * + **********************************/ + + template + auto linear_assigner::alignment_begin(const E1& e1, size_type size) const -> size_type + { + size_type align_begin = is_aligned ? 0 : xt_simd::get_alignment_offset(e1.data(), size, simd_size); + return align_begin; + } + + template + auto linear_assigner::alignment_end(size_type align_begin, size_type size) const -> size_type + { + size_type align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); + return align_end; + } + + template + void linear_assigner::assign_elements(E1& e1, const E2& e2, size_type begin, size_type end) const + { + for (size_type i = begin; i < end; ++i) + { + e1.data_element(i) = conditional_cast(e2.data_element(i)); + } + } + + template + void linear_assigner::assign_simd(E1& e1, const E2& e2, size_type begin, size_type end) const + { + for (size_type i = begin; i < end; i += simd_size) + { + e1.template store_simd(i, e2.template load_simd(i)); + } + } + + template + void + linear_assigner::assign_elements_no_simd(E1& e1, const E2& e2, size_type begin, size_type end) const + { + // If the types are not compatible, this function is still instantiated but never called. + // The loop must not be instantiated in that case to avoid compilation errors. + if constexpr (!is_convertible) + { + XTENSOR_PRECONDITION(false, "Internal error: linear_assigner called with unrelated types."); + } + else + { + using difference_type = std::ptrdiff_t; + auto src = linear_begin(e2) + static_cast(begin); + auto dst = linear_begin(e1) + static_cast(begin); + for (size_type i = begin; i < end; ++i) + { + *dst = static_cast(*src); + ++src; + ++dst; + } + } + } + + /*********************************** + * stepper_assigner implementation * + ***********************************/ + + template + inline stepper_assigner::stepper_assigner(E1& e1, const E2& e2) + : m_e1(e1) + , m_lhs(e1.stepper_begin(e1.shape())) + , m_rhs(e2.stepper_begin(e1.shape())) + , m_index(xtl::make_sequence(e1.shape().size(), size_type(0))) + { + } + + template + inline void stepper_assigner::run() + { + using tmp_size_type = typename E1::size_type; + using argument_type = std::decay_t; + using result_type = std::decay_t; + constexpr bool needs_cast = has_assign_conversion::value; + + tmp_size_type s = m_e1.size(); + for (tmp_size_type i = 0; i < s; ++i) + { + *m_lhs = conditional_cast(*m_rhs); + stepper_tools::increment_stepper(*this, m_index, m_e1.shape()); + } + } + + template + inline void stepper_assigner::step(size_type i) + { + m_lhs.step(i); + m_rhs.step(i); + } + + template + inline void stepper_assigner::step(size_type i, size_type n) + { + m_lhs.step(i, n); + m_rhs.step(i, n); + } + + template + inline void stepper_assigner::reset(size_type i) + { + m_lhs.reset(i); + m_rhs.reset(i); + } + + template + inline void stepper_assigner::to_end(layout_type l) + { + m_lhs.to_end(l); + m_rhs.to_end(l); + } + + /**************************************** + * strided_loop_assigner implementation * + ****************************************/ + + namespace strided_assign_detail + { + template + struct idx_tools; + + template <> + struct idx_tools + { + template + static void next_idx(T& outer_index, const T& outer_shape) + { + auto i = outer_index.size(); + for (; i > 0; --i) + { + if (outer_index[i - 1] + 1 >= outer_shape[i - 1]) + { + outer_index[i - 1] = 0; + } + else + { + outer_index[i - 1]++; + break; + } + } + } + + template + static void nth_idx(size_t n, T& outer_index, const T& outer_shape) + { + dynamic_shape stride_sizes; + xt::resize_container(stride_sizes, outer_shape.size()); + // compute strides + using size_type = typename T::size_type; + for (size_type i = outer_shape.size(); i > 0; i--) + { + stride_sizes[i - 1] = (i == outer_shape.size()) ? 1 : stride_sizes[i] * outer_shape[i]; + } + + // compute index + for (size_type i = 0; i < outer_shape.size(); i++) + { + auto d_idx = n / stride_sizes[i]; + outer_index[i] = d_idx; + n -= d_idx * stride_sizes[i]; + } + } + }; + + template <> + struct idx_tools + { + template + static void next_idx(T& outer_index, const T& outer_shape) + { + using size_type = typename T::size_type; + size_type i = 0; + auto sz = outer_index.size(); + for (; i < sz; ++i) + { + if (outer_index[i] + 1 >= outer_shape[i]) + { + outer_index[i] = 0; + } + else + { + outer_index[i]++; + break; + } + } + } + + template + static void nth_idx(size_t n, T& outer_index, const T& outer_shape) + { + dynamic_shape stride_sizes; + xt::resize_container(stride_sizes, outer_shape.size()); + + using size_type = typename T::size_type; + + // compute required strides + for (size_type i = 0; i < outer_shape.size(); i++) + { + stride_sizes[i] = (i == 0) ? 1 : stride_sizes[i - 1] * outer_shape[i - 1]; + } + + // compute index + for (size_type i = outer_shape.size(); i > 0;) + { + i--; + auto d_idx = n / stride_sizes[i]; + outer_index[i] = d_idx; + n -= d_idx * stride_sizes[i]; + } + } + }; + + template + struct check_strides_functor + { + using strides_type = S; + + check_strides_functor(const S& strides) + : m_cut(L == layout_type::row_major ? 0 : strides.size()) + , m_strides(strides) + { + } + + template + std::enable_if_t operator()(const T& el) + { + // All dimenions less than var have differing strides + auto var = check_strides_overlap::get(m_strides, el.strides()); + if (var > m_cut) + { + m_cut = var; + } + return m_cut; + } + + template + std::enable_if_t operator()(const T& el) + { + auto var = check_strides_overlap::get(m_strides, el.strides()); + // All dimensions >= var have differing strides + if (var < m_cut) + { + m_cut = var; + } + return m_cut; + } + + template + std::size_t operator()(const xt::xscalar& /*el*/) + { + return m_cut; + } + + template + std::size_t operator()(const xt::xfunction& xf) + { + xt::for_each(*this, xf.arguments()); + return m_cut; + } + + private: + + std::size_t m_cut; + const strides_type& m_strides; + }; + + template || !possible, bool> = true> + loop_sizes_t get_loop_sizes(const E1& e1, const E2&) + { + return {false, true, 1, e1.size(), e1.dimension(), e1.dimension()}; + } + + template && possible, bool> = true> + loop_sizes_t get_loop_sizes(const E1& e1, const E2& e2) + { + using shape_value_type = typename E1::shape_type::value_type; + bool is_row_major = true; + + // Try to find a row-major scheme first, where the outer loop is on the first N = `cut` + // dimensions, and the inner loop is + is_row_major = true; + auto is_zero = [](auto i) + { + return i == 0; + }; + auto&& strides = e1.strides(); + auto it_bwd = std::find_if_not(strides.rbegin(), strides.rend(), is_zero); + bool de1_row_contiguous = it_bwd != strides.rend() && *it_bwd == 1; + auto it_fwd = std::find_if_not(strides.begin(), strides.end(), is_zero); + bool de1_col_contiguous = it_fwd != strides.end() && *it_fwd == 1; + if (de1_row_contiguous) + { + is_row_major = true; + } + else if (de1_col_contiguous) + { + is_row_major = false; + } + else + { + // No strided loop possible. + return {false, true, 1, e1.size(), e1.dimension(), e1.dimension()}; + } + + // Cut is the number of dimensions in the outer loop + std::size_t cut = 0; + + if (is_row_major) + { + auto csf = check_strides_functor(e1.strides()); + cut = csf(e2); + // This makes that only one dimension will be treated in the inner loop. + if (cut < e1.strides().size() - 1) + { + // Only make the inner loop go over one dimension by default for now + cut = e1.strides().size() - 1; + } + } + else if (!is_row_major) + { + auto csf = check_strides_functor(e1.strides() + ); + cut = csf(e2); + if (cut > 1) + { + // Only make the inner loop go over one dimension by default for now + cut = 1; + } + } // can't reach here because this would have already triggered the fallback + + std::size_t outer_loop_size = static_cast(std::accumulate( + e1.shape().begin(), + e1.shape().begin() + static_cast(cut), + shape_value_type(1), + std::multiplies{} + )); + std::size_t inner_loop_size = static_cast(std::accumulate( + e1.shape().begin() + static_cast(cut), + e1.shape().end(), + shape_value_type(1), + std::multiplies{} + )); + + if (!is_row_major) + { + std::swap(outer_loop_size, inner_loop_size); + } + + return {inner_loop_size > 1, is_row_major, inner_loop_size, outer_loop_size, cut, e1.dimension()}; + } + } + + template + inline strided_loop_assigner::strided_loop_assigner(E1& e1, const E2& e2) + : m_e1(e1) + , m_e2(e2) + , m_loop_sizes(strided_assign_detail::get_loop_sizes(e1, e2)) + , m_max_shape() + , m_step_dim(0) + , m_simd_size(0) + , m_simd_rest(0) + { + if (!m_loop_sizes.can_do_strided_assign) + { + return; + } + + auto cut = static_cast(m_loop_sizes.cut); + if (m_loop_sizes.is_row_major) + { + m_max_shape.assign(e1.shape().begin(), e1.shape().begin() + cut); + } + else + { + m_max_shape.assign(e1.shape().begin() + cut, e1.shape().end()); + m_step_dim = m_loop_sizes.cut; + } + + m_simd_size = m_loop_sizes.inner_loop_size / simd_type::size; + m_simd_rest = m_loop_sizes.inner_loop_size % simd_type::size; + } + + template + inline bool strided_loop_assigner::can_do_strided_assign() const noexcept + { + return m_loop_sizes.can_do_strided_assign; + } + + template + inline auto strided_loop_assigner::inner_loop_size() const noexcept -> size_type + { + return m_loop_sizes.inner_loop_size; + } + + template + inline auto strided_loop_assigner::outer_loop_size() const noexcept -> size_type + { + return m_loop_sizes.outer_loop_size; + } + + template + inline auto strided_loop_assigner::make_cursor() const -> cursor + { + return cursor(*this); + } + + template + inline void strided_loop_assigner::run(size_type begin, size_type end) const + { + cursor c(*this); + for (size_type ox = begin; ox < end; ++ox) + { + c.assign_row(ox); + } + } + + template + inline void strided_loop_assigner::nth_idx(size_type n, index_type& index) const + { + if (m_loop_sizes.is_row_major) + { + strided_assign_detail::idx_tools::nth_idx(n, index, m_max_shape); + } + else + { + strided_assign_detail::idx_tools::nth_idx(n, index, m_max_shape); + } + } + + template + inline void strided_loop_assigner::next_idx(index_type& index) const + { + if (m_loop_sizes.is_row_major) + { + strided_assign_detail::idx_tools::next_idx(index, m_max_shape); + } + else + { + strided_assign_detail::idx_tools::next_idx(index, m_max_shape); + } + } + + template + inline strided_loop_assigner::cursor::cursor(const strided_loop_assigner& assigner) + : p_assigner(&assigner) + , m_lhs(assigner.m_e1.stepper_begin(assigner.m_e1.shape())) + , m_rhs(assigner.m_e2.stepper_begin(assigner.m_e1.shape())) + , m_index(xtl::make_sequence(assigner.m_max_shape.size(), size_type(0))) + , m_positioned(false) + { + } + + template + inline void strided_loop_assigner::cursor::assign_row(size_type ox) + { + const strided_loop_assigner& a = *p_assigner; + + if (!m_positioned) + { + if (ox != size_type(0)) + { + a.nth_idx(ox, m_index); + step_to_index(); + } + m_positioned = true; + } + + for (size_type i = 0; i < a.m_simd_size; ++i) + { + m_lhs.store_simd(m_rhs.template step_simd()); + } + for (size_type i = 0; i < a.m_simd_rest; ++i) + { + *m_lhs = conditional_cast(*m_rhs); + m_lhs.step_leading(); + m_rhs.step_leading(); + } + + a.next_idx(m_index); + + // need to step E1 as well if not contiguous assign (e.g. view) + if (!E1::contiguous_layout) + { + m_lhs.to_begin(); + m_rhs.to_begin(); + step_to_index(); + } + else + { + m_rhs.to_begin(); + for (size_type i = 0; i < m_index.size(); ++i) + { + m_rhs.step(i + a.m_step_dim, m_index[i]); + } + } + } + + template + inline void strided_loop_assigner::cursor::step_to_index() + { + size_type step_dim = p_assigner->m_step_dim; + for (size_type i = 0; i < m_index.size(); ++i) + { + m_lhs.step(i + step_dim, m_index[i]); + m_rhs.step(i + step_dim, m_index[i]); + } + } +} diff --git a/include/xtensor/core/xnoalias.hpp b/include/xtensor/core/xnoalias.hpp deleted file mode 100644 index 7d95a3b1a..000000000 --- a/include/xtensor/core/xnoalias.hpp +++ /dev/null @@ -1,230 +0,0 @@ -/*************************************************************************** - * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * - * Copyright (c) QuantStack * - * * - * Distributed under the terms of the BSD 3-Clause License. * - * * - * The full license is in the file LICENSE, distributed with this software. * - ****************************************************************************/ - -#ifndef XTENSOR_NOALIAS_HPP -#define XTENSOR_NOALIAS_HPP - -#include "../core/xsemantic.hpp" - -namespace xt -{ - - template - class noalias_proxy - { - public: - - noalias_proxy(A a) noexcept; - - template - disable_xexpression operator=(const E&); - - template - disable_xexpression operator+=(const E&); - - template - disable_xexpression operator-=(const E&); - - template - disable_xexpression operator*=(const E&); - - template - disable_xexpression operator/=(const E&); - - template - disable_xexpression operator%=(const E&); - - template - disable_xexpression operator&=(const E&); - - template - disable_xexpression operator|=(const E&); - - template - disable_xexpression operator^=(const E&); - - template - A operator=(const xexpression& e); - - template - A operator+=(const xexpression& e); - - template - A operator-=(const xexpression& e); - - template - A operator*=(const xexpression& e); - - template - A operator/=(const xexpression& e); - - template - A operator%=(const xexpression& e); - - template - A operator&=(const xexpression&); - - template - A operator|=(const xexpression&); - - template - A operator^=(const xexpression&); - - private: - - A m_array; - }; - - template - noalias_proxy> noalias(A&& a) noexcept; - - /******************************** - * noalias_proxy implementation * - ********************************/ - - template - inline noalias_proxy::noalias_proxy(A a) noexcept - : m_array(std::forward(a)) - { - } - - template - template - inline auto noalias_proxy::operator=(const E& e) -> disable_xexpression - { - return m_array.assign(xscalar(e)); - } - - template - template - inline auto noalias_proxy::operator+=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::plus<>()); - } - - template - template - inline auto noalias_proxy::operator-=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::minus<>()); - } - - template - template - inline auto noalias_proxy::operator*=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::multiplies<>()); - } - - template - template - inline auto noalias_proxy::operator/=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::divides<>()); - } - - template - template - inline auto noalias_proxy::operator%=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::modulus<>()); - } - - template - template - inline auto noalias_proxy::operator&=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::bit_and<>()); - } - - template - template - inline auto noalias_proxy::operator|=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::bit_or<>()); - } - - template - template - inline auto noalias_proxy::operator^=(const E& e) -> disable_xexpression - { - return m_array.scalar_computed_assign(e, std::bit_xor<>()); - } - - template - template - inline A noalias_proxy::operator=(const xexpression& e) - { - return m_array.assign(e); - } - - template - template - inline A noalias_proxy::operator+=(const xexpression& e) - { - return m_array.plus_assign(e); - } - - template - template - inline A noalias_proxy::operator-=(const xexpression& e) - { - return m_array.minus_assign(e); - } - - template - template - inline A noalias_proxy::operator*=(const xexpression& e) - { - return m_array.multiplies_assign(e); - } - - template - template - inline A noalias_proxy::operator/=(const xexpression& e) - { - return m_array.divides_assign(e); - } - - template - template - inline A noalias_proxy::operator%=(const xexpression& e) - { - return m_array.modulus_assign(e); - } - - template - template - inline A noalias_proxy::operator&=(const xexpression& e) - { - return m_array.bit_and_assign(e); - } - - template - template - inline A noalias_proxy::operator|=(const xexpression& e) - { - return m_array.bit_or_assign(e); - } - - template - template - inline A noalias_proxy::operator^=(const xexpression& e) - { - return m_array.bit_xor_assign(e); - } - - template - inline noalias_proxy> noalias(A&& a) noexcept - { - return noalias_proxy>(a); - } -} - -#endif diff --git a/include/xtensor/core/xsemantic.hpp b/include/xtensor/core/xsemantic.hpp index c9f59670a..a935899ee 100644 --- a/include/xtensor/core/xsemantic.hpp +++ b/include/xtensor/core/xsemantic.hpp @@ -14,6 +14,7 @@ #include #include "../core/xassign.hpp" +#include "../core/xassign_proxy.hpp" #include "../core/xexpression_traits.hpp" namespace xt @@ -111,32 +112,32 @@ namespace xt template derived_type& operator^=(const xexpression&); - template - derived_type& assign(const xexpression&); + template + derived_type& assign(const xexpression& e, P p); - template - derived_type& plus_assign(const xexpression&); + template + derived_type& plus_assign(const xexpression& e, P p); - template - derived_type& minus_assign(const xexpression&); + template + derived_type& minus_assign(const xexpression& e, P p); - template - derived_type& multiplies_assign(const xexpression&); + template + derived_type& multiplies_assign(const xexpression& e, P p); - template - derived_type& divides_assign(const xexpression&); + template + derived_type& divides_assign(const xexpression& e, P p); - template - derived_type& modulus_assign(const xexpression&); + template + derived_type& modulus_assign(const xexpression& e, P p); - template - derived_type& bit_and_assign(const xexpression&); + template + derived_type& bit_and_assign(const xexpression& e, P p); - template - derived_type& bit_or_assign(const xexpression&); + template + derived_type& bit_or_assign(const xexpression& e, P p); - template - derived_type& bit_xor_assign(const xexpression&); + template + derived_type& bit_xor_assign(const xexpression& e, P p); protected: @@ -184,14 +185,14 @@ namespace xt derived_type& assign_temporary(temporary_type&&); - template - derived_type& assign_xexpression(const xexpression& e); + template + derived_type& assign_xexpression(const xexpression& e, P p = P{}); - template - derived_type& computed_assign(const xexpression& e); + template > + derived_type& computed_assign(const xexpression& e, P p = P{}); - template - derived_type& scalar_computed_assign(const E& e, F&& f); + template > + derived_type& scalar_computed_assign(const E& e, F&& f, P p = P{}); protected: @@ -261,14 +262,14 @@ namespace xt derived_type& assign_temporary(temporary_type&&); - template - derived_type& assign_xexpression(const xexpression& e); + template + derived_type& assign_xexpression(const xexpression& e, P p = P{}); - template - derived_type& computed_assign(const xexpression& e); + template > + derived_type& computed_assign(const xexpression& e, P p = P{}); - template - derived_type& scalar_computed_assign(const E& e, F&& f); + template > + derived_type& scalar_computed_assign(const E& e, F&& f, P p = P{}); protected: @@ -507,10 +508,33 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().assign_xexpression(e); + if constexpr (has_aliasing

) + { +#ifdef XTENSOR_FORCE_TEMPORARY_MEMORY_IN_ASSIGNMENTS + temporary_type tmp(e); + return this->derived_cast().assign_temporary(std::move(tmp)); +#else + auto&& this_derived = this->derived_cast(); + auto memory_checker = make_overlapping_memory_checker(this_derived); + if (memory_checker.check_overlap(e.derived_cast())) + { + temporary_type tmp(e); + return this_derived.assign_temporary(std::move(tmp)); + } + else + { + using new_policy = assign_policy; + return this->derived_cast().assign_xexpression(e, new_policy{}); + } +#endif + } + else + { + return this->derived_cast().assign_xexpression(e, p); + } } /** @@ -520,10 +544,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::plus_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::plus_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() + e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() + e.derived_cast(), p); } /** @@ -533,10 +557,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::minus_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::minus_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() - e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() - e.derived_cast(), p); } /** @@ -546,10 +570,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::multiplies_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::multiplies_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() * e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() * e.derived_cast(), p); } /** @@ -559,10 +583,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::divides_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::divides_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() / e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() / e.derived_cast(), p); } /** @@ -572,10 +596,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::modulus_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::modulus_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() % e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() % e.derived_cast(), p); } /** @@ -585,10 +609,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::bit_and_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::bit_and_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() & e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() & e.derived_cast(), p); } /** @@ -598,10 +622,10 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::bit_or_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::bit_or_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() | e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() | e.derived_cast(), p); } /** @@ -611,32 +635,17 @@ namespace xt * @return a reference to \c *this. */ template - template - inline auto xsemantic_base::bit_xor_assign(const xexpression& e) -> derived_type& + template + inline auto xsemantic_base::bit_xor_assign(const xexpression& e, P p) -> derived_type& { - return this->derived_cast().computed_assign(this->derived_cast() ^ e.derived_cast()); + return this->derived_cast().computed_assign(this->derived_cast() ^ e.derived_cast(), p); } template template inline auto xsemantic_base::operator=(const xexpression& e) -> derived_type& { -#ifdef XTENSOR_FORCE_TEMPORARY_MEMORY_IN_ASSIGNMENTS - temporary_type tmp(e); - return this->derived_cast().assign_temporary(std::move(tmp)); -#else - auto&& this_derived = this->derived_cast(); - auto memory_checker = make_overlapping_memory_checker(this_derived); - if (memory_checker.check_overlap(e.derived_cast())) - { - temporary_type tmp(e); - return this_derived.assign_temporary(std::move(tmp)); - } - else - { - return this->assign(e); - } -#endif + return this->assign(e, default_assign); } /************************************** @@ -655,26 +664,26 @@ namespace xt } template - template - inline auto xcontainer_semantic::assign_xexpression(const xexpression& e) -> derived_type& + template + inline auto xcontainer_semantic::assign_xexpression(const xexpression& e, P p) -> derived_type& { - xt::assign_to_container(*this, e); + xt::assign_to_container(*this, e, p); return this->derived_cast(); } template - template - inline auto xcontainer_semantic::computed_assign(const xexpression& e) -> derived_type& + template + inline auto xcontainer_semantic::computed_assign(const xexpression& e, P p) -> derived_type& { - xt::computed_assign(*this, e); + xt::computed_assign(*this, e, p); return this->derived_cast(); } template - template - inline auto xcontainer_semantic::scalar_computed_assign(const E& e, F&& f) -> derived_type& + template + inline auto xcontainer_semantic::scalar_computed_assign(const E& e, F&& f, P p) -> derived_type& { - xt::scalar_computed_assign(*this, e, std::forward(f)); + xt::scalar_computed_assign(*this, e, std::forward(f), p); return this->derived_cast(); } @@ -702,18 +711,18 @@ namespace xt } template - template - inline auto xview_semantic::assign_xexpression(const xexpression& e) -> derived_type& + template + inline auto xview_semantic::assign_xexpression(const xexpression& e, P p) -> derived_type& { - xt::assign_to_view(*this, e); + xt::assign_to_view(*this, e, p); return this->derived_cast(); } template - template - inline auto xview_semantic::computed_assign(const xexpression& e) -> derived_type& + template + inline auto xview_semantic::computed_assign(const xexpression& e, P p) -> derived_type& { - xt::assign_to_view(*this, e); + xt::assign_to_view(*this, e, p); return this->derived_cast(); } @@ -733,8 +742,8 @@ namespace xt } template - template - inline auto xview_semantic::scalar_computed_assign(const E& e, F&& f) -> derived_type& + template + inline auto xview_semantic::scalar_computed_assign(const E& e, F&& f, P) -> derived_type& { D& d = this->derived_cast(); diff --git a/include/xtensor/misc/xfft.hpp b/include/xtensor/misc/xfft.hpp index caf37145e..bbe61f594 100644 --- a/include/xtensor/misc/xfft.hpp +++ b/include/xtensor/misc/xfft.hpp @@ -7,7 +7,6 @@ #include "../containers/xarray.hpp" #include "../core/xmath.hpp" -#include "../core/xnoalias.hpp" #include "../generators/xbuilder.hpp" #include "../misc/xcomplex.hpp" #include "../views/xaxis_slice_iterator.hpp" diff --git a/include/xtensor/optional/xoptional.hpp b/include/xtensor/optional/xoptional.hpp index 6c690b0d6..afe9906ce 100644 --- a/include/xtensor/optional/xoptional.hpp +++ b/include/xtensor/optional/xoptional.hpp @@ -264,8 +264,8 @@ namespace xt { public: - template - static void assign_data(xexpression& e1, const xexpression& e2, bool trivial); + template + static void assign_data(xexpression& e1, const xexpression& e2, bool trivial, P); }; /********************************** @@ -1279,24 +1279,24 @@ namespace xt template struct assign_data_impl { - template - static void run(xexpression& e1, const xexpression& e2, bool trivial) + template + static void run(xexpression& e1, const xexpression& e2, bool trivial, P p) { E1& de1 = e1.derived_cast(); const E2& de2 = e2.derived_cast(); decltype(auto) bde1 = xt::value(de1); decltype(auto) hde1 = xt::has_value(de1); - xexpression_assigner_base::assign_data(bde1, xt::value(de2), trivial); - xexpression_assigner_base::assign_data(hde1, xt::has_value(de2), trivial); + xexpression_assigner_base::assign_data(bde1, xt::value(de2), trivial, p); + xexpression_assigner_base::assign_data(hde1, xt::has_value(de2), trivial, p); } }; template struct xarray_assigner { - template - static void assign(xexpression& e1, const xexpression& e2, bool trivial) + template + static void assign(xexpression& e1, const xexpression& e2, bool trivial, P p) { E1& de1 = e1.derived_cast(); const E2& de2 = e2.derived_cast(); @@ -1304,50 +1304,51 @@ namespace xt decltype(auto) bde1 = xt::value(de1); decltype(auto) hde1 = xt::has_value(de1); - xexpression_assigner_base::assign_data(bde1, e2, trivial); - xexpression_assigner_base::assign_data(hde1, mask, trivial); + xexpression_assigner_base::assign_data(bde1, e2, trivial, p); + xexpression_assigner_base::assign_data(hde1, mask, trivial, p); } }; template struct xarray_assigner> { - template - static void assign(xexpression& e1, const xexpression& e2, bool trivial) + template + static void assign(xexpression& e1, const xexpression& e2, bool trivial, P p) { - xexpression_assigner_base::assign_data(e1, e2, trivial); + xexpression_assigner_base::assign_data(e1, e2, trivial, p); } }; template <> struct assign_data_impl { - template - static void run(xexpression& e1, const xexpression& e2, bool trivial) + template + static void run(xexpression& e1, const xexpression& e2, bool trivial, P p) { - xarray_assigner::assign(e1, e2, trivial); + xarray_assigner::assign(e1, e2, trivial, p); } }; template <> struct assign_data_impl { - template - static void run(xexpression& e1, const xexpression& e2, bool trivial) + template + static void run(xexpression& e1, const xexpression& e2, bool trivial, P p) { - xexpression_assigner_base::assign_data(e1, e2, trivial); + xexpression_assigner_base::assign_data(e1, e2, trivial, p); } }; } - template + template inline void xexpression_assigner_base::assign_data( xexpression& e1, const xexpression& e2, - bool trivial + bool trivial, + P p ) { - detail::assign_data_impl::run(e1, e2, trivial); + detail::assign_data_impl::run(e1, e2, trivial, p); } } diff --git a/include/xtensor/optional/xoptional_assembly.hpp b/include/xtensor/optional/xoptional_assembly.hpp index 062a1f9fa..defc5b0b7 100644 --- a/include/xtensor/optional/xoptional_assembly.hpp +++ b/include/xtensor/optional/xoptional_assembly.hpp @@ -567,7 +567,7 @@ namespace xt , m_has_value() , m_storage(m_value.storage(), m_has_value.storage()) { - semantic_base::assign(e); + semantic_base::assign(e, noalias); } /** diff --git a/include/xtensor/views/xstrided_view.hpp b/include/xtensor/views/xstrided_view.hpp index b0fe2c6e0..f894d422b 100644 --- a/include/xtensor/views/xstrided_view.hpp +++ b/include/xtensor/views/xstrided_view.hpp @@ -414,28 +414,21 @@ namespace xt return *this; } - namespace xstrided_view_detail - { - template - inline void run_assign_temporary_impl(V& v, const T& t, std::true_type /* enable strided assign */) - { - strided_loop_assigner::run(v, t); - } - - template - inline void - run_assign_temporary_impl(V& v, const T& t, std::false_type /* fallback to iterator assign */) - { - std::copy(t.cbegin(), t.cend(), v.begin()); - } - } - template inline void xstrided_view::assign_temporary_impl(temporary_type&& tmp) { constexpr bool fast_assign = xassign_traits, temporary_type>::simd_strided_assign(); - xstrided_view_detail::run_assign_temporary_impl(*this, tmp, std::integral_constant{}); + if constexpr (fast_assign) + { + using self_type = xstrided_view; + parallel_assigner_t assigner; + assigner.strided_loop_assign(*this, tmp); + } + else + { + std::copy(tmp.cbegin(), tmp.cend(), this->begin()); + } } /** diff --git a/include/xtensor/views/xview.hpp b/include/xtensor/views/xview.hpp index c647572de..1739a5cc4 100644 --- a/include/xtensor/views/xview.hpp +++ b/include/xtensor/views/xview.hpp @@ -1746,28 +1746,21 @@ namespace xt return shape; } - namespace xview_detail - { - template - inline void run_assign_temporary_impl(V& v, const T& t, std::true_type /* enable strided assign */) - { - strided_loop_assigner::run(v, t); - } - - template - inline void - run_assign_temporary_impl(V& v, const T& t, std::false_type /* fallback to iterator assign */) - { - std::copy(t.cbegin(), t.cend(), v.begin()); - } - } - template inline void xview::assign_temporary_impl(temporary_type&& tmp) { constexpr bool fast_assign = detail::is_strided_view::value && xassign_traits, temporary_type>::simd_strided_assign(); - xview_detail::run_assign_temporary_impl(*this, tmp, std::integral_constant{}); + if constexpr (fast_assign) + { + using self_type = xview; + parallel_assigner_t assigner; + assigner.strided_loop_assign(*this, tmp); + } + else + { + std::copy(tmp.cbegin(), tmp.cend(), this->begin()); + } } namespace detail diff --git a/test/files/cppy_source/test_extended_broadcast_view.cppy b/test/files/cppy_source/test_extended_broadcast_view.cppy index fee8395f2..ca8ee8f23 100644 --- a/test/files/cppy_source/test_extended_broadcast_view.cppy +++ b/test/files/cppy_source/test_extended_broadcast_view.cppy @@ -11,7 +11,6 @@ #include "xtensor/xarray.hpp" #include "xtensor/xfixed.hpp" -#include "xtensor/xnoalias.hpp" #include "xtensor/xstrided_view.hpp" #include "xtensor/xtensor.hpp" #include "xtensor/xview.hpp" diff --git a/test/test_extended_broadcast_view.cpp b/test/test_extended_broadcast_view.cpp index a0237255c..fb52ab6f2 100644 --- a/test/test_extended_broadcast_view.cpp +++ b/test/test_extended_broadcast_view.cpp @@ -15,7 +15,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/views/xstrided_view.hpp" #include "xtensor/views/xview.hpp" diff --git a/test/test_strided_assign.cpp b/test/test_strided_assign.cpp index 3f79a754f..482a4c30e 100644 --- a/test/test_strided_assign.cpp +++ b/test/test_strided_assign.cpp @@ -15,7 +15,6 @@ #include "xtensor/containers/xtensor.hpp" #include "xtensor/core/xassign.hpp" #include "xtensor/core/xlayout.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/views/xview.hpp" #include "test_common.hpp" diff --git a/test/test_xassign.cpp b/test/test_xassign.cpp index 3535b4886..de81c6dec 100644 --- a/test/test_xassign.cpp +++ b/test/test_xassign.cpp @@ -14,7 +14,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" #include "xtensor/core/xassign.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/misc/xmanipulation.hpp" #include "test_common.hpp" diff --git a/test/test_xchunked_array.cpp b/test/test_xchunked_array.cpp index 8be647c8e..b36f91b9f 100644 --- a/test/test_xchunked_array.cpp +++ b/test/test_xchunked_array.cpp @@ -8,7 +8,6 @@ ****************************************************************************/ #include "xtensor/chunk/xchunked_array.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/io/xcsv.hpp" #include "xtensor/views/xbroadcast.hpp" diff --git a/test/test_xdynamic_view.cpp b/test/test_xdynamic_view.cpp index dff43d80d..3874acf36 100644 --- a/test/test_xdynamic_view.cpp +++ b/test/test_xdynamic_view.cpp @@ -9,7 +9,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/io/xio.hpp" diff --git a/test/test_xfixed.cpp b/test/test_xfixed.cpp index 3a6e8181b..ed52b254c 100644 --- a/test/test_xfixed.cpp +++ b/test/test_xfixed.cpp @@ -19,7 +19,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/misc/xmanipulation.hpp" #include "xtensor/views/xview.hpp" diff --git a/test/test_xfunctor_adaptor.cpp b/test/test_xfunctor_adaptor.cpp index caa12d34f..103f7c825 100644 --- a/test/test_xfunctor_adaptor.cpp +++ b/test/test_xfunctor_adaptor.cpp @@ -8,7 +8,6 @@ ****************************************************************************/ #include "xtensor/containers/xarray.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/misc/xcomplex.hpp" #include "xtensor/views/xfunctor_view.hpp" diff --git a/test/test_xnoalias.cpp b/test/test_xnoalias.cpp index 18e4e0c0b..539f9e83a 100644 --- a/test/test_xnoalias.cpp +++ b/test/test_xnoalias.cpp @@ -8,7 +8,6 @@ ****************************************************************************/ #include "xtensor/containers/xarray.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/views/xview.hpp" diff --git a/test/test_xnorm.cpp b/test/test_xnorm.cpp index 632114530..5d09aca4c 100644 --- a/test/test_xnorm.cpp +++ b/test/test_xnorm.cpp @@ -10,7 +10,6 @@ #include #include "xtensor/containers/xarray.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" #include "xtensor/reducers/xnorm.hpp" #include "xtensor/views/xview.hpp" diff --git a/test/test_xscalar_semantic.cpp b/test/test_xscalar_semantic.cpp index 9b7223b70..887f1e493 100644 --- a/test/test_xscalar_semantic.cpp +++ b/test/test_xscalar_semantic.cpp @@ -7,8 +7,6 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ -#include "xtensor/core/xnoalias.hpp" - #include "test_common_macros.hpp" #include "test_xsemantic.hpp" diff --git a/test/test_xstrided_view.cpp b/test/test_xstrided_view.cpp index 1d3906980..b369db516 100644 --- a/test/test_xstrided_view.cpp +++ b/test/test_xstrided_view.cpp @@ -10,7 +10,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/misc/xmanipulation.hpp" diff --git a/test/test_xview.cpp b/test/test_xview.cpp index 5f00d244f..db98088ea 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -24,7 +24,6 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xfixed.hpp" #include "xtensor/containers/xtensor.hpp" -#include "xtensor/core/xnoalias.hpp" #include "xtensor/generators/xbuilder.hpp" #include "xtensor/generators/xrandom.hpp" #include "xtensor/misc/xmanipulation.hpp" @@ -1494,7 +1493,7 @@ namespace xt b = xt::view(a, xt::all(), xt::range(0, 1)); break; case 2: - b.assign(xt::view(a, xt::all(), xt::range(0, 1))); + b.assign(xt::view(a, xt::all(), xt::range(0, 1)), noalias); break; case 3: b = xt::view(a, xt::all(), xt::range(0, 2)); diff --git a/test/test_xview_semantic.cpp b/test/test_xview_semantic.cpp index 83207ec59..57070c965 100644 --- a/test/test_xview_semantic.cpp +++ b/test/test_xview_semantic.cpp @@ -7,7 +7,6 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ -#include "xtensor/core/xnoalias.hpp" #include "xtensor/views/xview.hpp" #include "test_common_macros.hpp" From 8c7bfa240be545587bf59915ca94a301b3005347 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Mon, 2 Nov 2026 16:04:19 +0100 Subject: [PATCH 3/3] Fixed benchmark regression --- include/xtensor/core/xassigner.hpp | 172 +++++++++++------------------ include/xtensor/core/xsemantic.hpp | 2 +- 2 files changed, 66 insertions(+), 108 deletions(-) diff --git a/include/xtensor/core/xassigner.hpp b/include/xtensor/core/xassigner.hpp index bd3dcefcc..dfa0a55b7 100644 --- a/include/xtensor/core/xassigner.hpp +++ b/include/xtensor/core/xassigner.hpp @@ -304,39 +304,13 @@ namespace xt xt_simd::simd_type>; static constexpr bool needs_cast = has_assign_conversion::value; - // Holds the per-thread state (steppers and outer index) required - // to assign a sequence of consecutive outer iterations. - class cursor - { - public: - - explicit cursor(const strided_loop_assigner& assigner); - - // Assigns the inner loop corresponding to the outer iteration ox, - // and moves to the next outer iteration. The first call positions - // the steppers on ox, subsequent calls must be made with ox + 1. - void assign_row(size_type ox); - - private: - - void step_to_index(); - - const strided_loop_assigner* p_assigner; - lhs_stepper m_lhs; - rhs_stepper m_rhs; - index_type m_index; - bool m_positioned; - }; - strided_loop_assigner(E1& e1, const E2& e2); bool can_do_strided_assign() const noexcept; size_type inner_loop_size() const noexcept; size_type outer_loop_size() const noexcept; - cursor make_cursor() const; - - // Assigns the outer iterations [begin, end) + // Assigns the outer iterations [begin, end). void run(size_type begin, size_type end) const; private: @@ -583,13 +557,17 @@ namespace xt size_type outer_loop_size = helper.outer_loop_size(); if (outer_loop_size >= XTENSOR_OPENMP_TRESHOLD / helper.inner_loop_size()) { - // Each thread gets its own copy of the cursor, which positions itself - // on the first outer iteration of the chunk assigned to the thread. - auto cursor = helper.make_cursor(); -#pragma omp parallel for schedule(static) firstprivate(cursor) - for (std::ptrdiff_t ox = 0; ox < static_cast(outer_loop_size); ++ox) + // Each thread assigns a contiguous chunk of outer iterations, + // as schedule(static) would do +#pragma omp parallel default(none) shared(helper, outer_loop_size) { - cursor.assign_row(static_cast(ox)); + size_type nb_threads = static_cast(omp_get_num_threads()); + size_type thread_id = static_cast(omp_get_thread_num()); + size_type chunk = outer_loop_size / nb_threads; + size_type rest = outer_loop_size % nb_threads; + size_type begin = thread_id * chunk + std::min(thread_id, rest); + size_type end = begin + chunk + (thread_id < rest ? 1u : 0u); + helper.run(begin, end); } } else @@ -1070,19 +1048,65 @@ namespace xt return m_loop_sizes.outer_loop_size; } - template - inline auto strided_loop_assigner::make_cursor() const -> cursor - { - return cursor(*this); - } - template inline void strided_loop_assigner::run(size_type begin, size_type end) const { - cursor c(*this); + if (begin >= end) + { + return; + } + + const size_type step_dim = m_step_dim; + const size_type simd_size = m_simd_size; + const size_type simd_rest = m_simd_rest; + + lhs_stepper lhs = m_e1.stepper_begin(m_e1.shape()); + rhs_stepper rhs = m_e2.stepper_begin(m_e1.shape()); + index_type index = xtl::make_sequence(m_max_shape.size(), size_type(0)); + + if (begin != size_type(0)) + { + nth_idx(begin, index); + for (size_type i = 0; i < index.size(); ++i) + { + lhs.step(i + step_dim, index[i]); + rhs.step(i + step_dim, index[i]); + } + } + for (size_type ox = begin; ox < end; ++ox) { - c.assign_row(ox); + for (size_type i = 0; i < simd_size; ++i) + { + lhs.store_simd(rhs.template step_simd()); + } + for (size_type i = 0; i < simd_rest; ++i) + { + *lhs = conditional_cast(*rhs); + lhs.step_leading(); + rhs.step_leading(); + } + + next_idx(index); + + // need to step E1 as well if not contiguous assign (e.g. view) + rhs.to_begin(); + if (!E1::contiguous_layout) + { + lhs.to_begin(); + for (size_type i = 0; i < index.size(); ++i) + { + lhs.step(i + step_dim, index[i]); + rhs.step(i + step_dim, index[i]); + } + } + else + { + for (size_type i = 0; i < index.size(); ++i) + { + rhs.step(i + step_dim, index[i]); + } + } } } @@ -1111,70 +1135,4 @@ namespace xt strided_assign_detail::idx_tools::next_idx(index, m_max_shape); } } - - template - inline strided_loop_assigner::cursor::cursor(const strided_loop_assigner& assigner) - : p_assigner(&assigner) - , m_lhs(assigner.m_e1.stepper_begin(assigner.m_e1.shape())) - , m_rhs(assigner.m_e2.stepper_begin(assigner.m_e1.shape())) - , m_index(xtl::make_sequence(assigner.m_max_shape.size(), size_type(0))) - , m_positioned(false) - { - } - - template - inline void strided_loop_assigner::cursor::assign_row(size_type ox) - { - const strided_loop_assigner& a = *p_assigner; - - if (!m_positioned) - { - if (ox != size_type(0)) - { - a.nth_idx(ox, m_index); - step_to_index(); - } - m_positioned = true; - } - - for (size_type i = 0; i < a.m_simd_size; ++i) - { - m_lhs.store_simd(m_rhs.template step_simd()); - } - for (size_type i = 0; i < a.m_simd_rest; ++i) - { - *m_lhs = conditional_cast(*m_rhs); - m_lhs.step_leading(); - m_rhs.step_leading(); - } - - a.next_idx(m_index); - - // need to step E1 as well if not contiguous assign (e.g. view) - if (!E1::contiguous_layout) - { - m_lhs.to_begin(); - m_rhs.to_begin(); - step_to_index(); - } - else - { - m_rhs.to_begin(); - for (size_type i = 0; i < m_index.size(); ++i) - { - m_rhs.step(i + a.m_step_dim, m_index[i]); - } - } - } - - template - inline void strided_loop_assigner::cursor::step_to_index() - { - size_type step_dim = p_assigner->m_step_dim; - for (size_type i = 0; i < m_index.size(); ++i) - { - m_lhs.step(i + step_dim, m_index[i]); - m_rhs.step(i + step_dim, m_index[i]); - } - } } diff --git a/include/xtensor/core/xsemantic.hpp b/include/xtensor/core/xsemantic.hpp index a935899ee..add76c62d 100644 --- a/include/xtensor/core/xsemantic.hpp +++ b/include/xtensor/core/xsemantic.hpp @@ -526,7 +526,7 @@ namespace xt } else { - using new_policy = assign_policy; + using new_policy = assign_policy; return this->derived_cast().assign_xexpression(e, new_policy{}); } #endif