Skip to content

Commit 8c7bfa2

Browse files
committed
Fixed benchmark regression
1 parent 041861b commit 8c7bfa2

2 files changed

Lines changed: 66 additions & 108 deletions

File tree

‎include/xtensor/core/xassigner.hpp‎

Lines changed: 65 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -304,39 +304,13 @@ namespace xt
304304
xt_simd::simd_type<value_type>>;
305305
static constexpr bool needs_cast = has_assign_conversion<e2_value_type, e1_value_type>::value;
306306

307-
// Holds the per-thread state (steppers and outer index) required
308-
// to assign a sequence of consecutive outer iterations.
309-
class cursor
310-
{
311-
public:
312-
313-
explicit cursor(const strided_loop_assigner& assigner);
314-
315-
// Assigns the inner loop corresponding to the outer iteration ox,
316-
// and moves to the next outer iteration. The first call positions
317-
// the steppers on ox, subsequent calls must be made with ox + 1.
318-
void assign_row(size_type ox);
319-
320-
private:
321-
322-
void step_to_index();
323-
324-
const strided_loop_assigner* p_assigner;
325-
lhs_stepper m_lhs;
326-
rhs_stepper m_rhs;
327-
index_type m_index;
328-
bool m_positioned;
329-
};
330-
331307
strided_loop_assigner(E1& e1, const E2& e2);
332308

333309
bool can_do_strided_assign() const noexcept;
334310
size_type inner_loop_size() const noexcept;
335311
size_type outer_loop_size() const noexcept;
336312

337-
cursor make_cursor() const;
338-
339-
// Assigns the outer iterations [begin, end)
313+
// Assigns the outer iterations [begin, end).
340314
void run(size_type begin, size_type end) const;
341315

342316
private:
@@ -583,13 +557,17 @@ namespace xt
583557
size_type outer_loop_size = helper.outer_loop_size();
584558
if (outer_loop_size >= XTENSOR_OPENMP_TRESHOLD / helper.inner_loop_size())
585559
{
586-
// Each thread gets its own copy of the cursor, which positions itself
587-
// on the first outer iteration of the chunk assigned to the thread.
588-
auto cursor = helper.make_cursor();
589-
#pragma omp parallel for schedule(static) firstprivate(cursor)
590-
for (std::ptrdiff_t ox = 0; ox < static_cast<std::ptrdiff_t>(outer_loop_size); ++ox)
560+
// Each thread assigns a contiguous chunk of outer iterations,
561+
// as schedule(static) would do
562+
#pragma omp parallel default(none) shared(helper, outer_loop_size)
591563
{
592-
cursor.assign_row(static_cast<size_type>(ox));
564+
size_type nb_threads = static_cast<size_type>(omp_get_num_threads());
565+
size_type thread_id = static_cast<size_type>(omp_get_thread_num());
566+
size_type chunk = outer_loop_size / nb_threads;
567+
size_type rest = outer_loop_size % nb_threads;
568+
size_type begin = thread_id * chunk + std::min(thread_id, rest);
569+
size_type end = begin + chunk + (thread_id < rest ? 1u : 0u);
570+
helper.run(begin, end);
593571
}
594572
}
595573
else
@@ -1070,19 +1048,65 @@ namespace xt
10701048
return m_loop_sizes.outer_loop_size;
10711049
}
10721050

1073-
template <class E1, class E2>
1074-
inline auto strided_loop_assigner<E1, E2>::make_cursor() const -> cursor
1075-
{
1076-
return cursor(*this);
1077-
}
1078-
10791051
template <class E1, class E2>
10801052
inline void strided_loop_assigner<E1, E2>::run(size_type begin, size_type end) const
10811053
{
1082-
cursor c(*this);
1054+
if (begin >= end)
1055+
{
1056+
return;
1057+
}
1058+
1059+
const size_type step_dim = m_step_dim;
1060+
const size_type simd_size = m_simd_size;
1061+
const size_type simd_rest = m_simd_rest;
1062+
1063+
lhs_stepper lhs = m_e1.stepper_begin(m_e1.shape());
1064+
rhs_stepper rhs = m_e2.stepper_begin(m_e1.shape());
1065+
index_type index = xtl::make_sequence<index_type>(m_max_shape.size(), size_type(0));
1066+
1067+
if (begin != size_type(0))
1068+
{
1069+
nth_idx(begin, index);
1070+
for (size_type i = 0; i < index.size(); ++i)
1071+
{
1072+
lhs.step(i + step_dim, index[i]);
1073+
rhs.step(i + step_dim, index[i]);
1074+
}
1075+
}
1076+
10831077
for (size_type ox = begin; ox < end; ++ox)
10841078
{
1085-
c.assign_row(ox);
1079+
for (size_type i = 0; i < simd_size; ++i)
1080+
{
1081+
lhs.store_simd(rhs.template step_simd<value_type>());
1082+
}
1083+
for (size_type i = 0; i < simd_rest; ++i)
1084+
{
1085+
*lhs = conditional_cast<needs_cast, e1_value_type>(*rhs);
1086+
lhs.step_leading();
1087+
rhs.step_leading();
1088+
}
1089+
1090+
next_idx(index);
1091+
1092+
// need to step E1 as well if not contiguous assign (e.g. view)
1093+
rhs.to_begin();
1094+
if (!E1::contiguous_layout)
1095+
{
1096+
lhs.to_begin();
1097+
for (size_type i = 0; i < index.size(); ++i)
1098+
{
1099+
lhs.step(i + step_dim, index[i]);
1100+
rhs.step(i + step_dim, index[i]);
1101+
}
1102+
}
1103+
else
1104+
{
1105+
for (size_type i = 0; i < index.size(); ++i)
1106+
{
1107+
rhs.step(i + step_dim, index[i]);
1108+
}
1109+
}
10861110
}
10871111
}
10881112

@@ -1111,70 +1135,4 @@ namespace xt
11111135
strided_assign_detail::idx_tools<layout_type::column_major>::next_idx(index, m_max_shape);
11121136
}
11131137
}
1114-
1115-
template <class E1, class E2>
1116-
inline strided_loop_assigner<E1, E2>::cursor::cursor(const strided_loop_assigner& assigner)
1117-
: p_assigner(&assigner)
1118-
, m_lhs(assigner.m_e1.stepper_begin(assigner.m_e1.shape()))
1119-
, m_rhs(assigner.m_e2.stepper_begin(assigner.m_e1.shape()))
1120-
, m_index(xtl::make_sequence<index_type>(assigner.m_max_shape.size(), size_type(0)))
1121-
, m_positioned(false)
1122-
{
1123-
}
1124-
1125-
template <class E1, class E2>
1126-
inline void strided_loop_assigner<E1, E2>::cursor::assign_row(size_type ox)
1127-
{
1128-
const strided_loop_assigner& a = *p_assigner;
1129-
1130-
if (!m_positioned)
1131-
{
1132-
if (ox != size_type(0))
1133-
{
1134-
a.nth_idx(ox, m_index);
1135-
step_to_index();
1136-
}
1137-
m_positioned = true;
1138-
}
1139-
1140-
for (size_type i = 0; i < a.m_simd_size; ++i)
1141-
{
1142-
m_lhs.store_simd(m_rhs.template step_simd<value_type>());
1143-
}
1144-
for (size_type i = 0; i < a.m_simd_rest; ++i)
1145-
{
1146-
*m_lhs = conditional_cast<needs_cast, e1_value_type>(*m_rhs);
1147-
m_lhs.step_leading();
1148-
m_rhs.step_leading();
1149-
}
1150-
1151-
a.next_idx(m_index);
1152-
1153-
// need to step E1 as well if not contiguous assign (e.g. view)
1154-
if (!E1::contiguous_layout)
1155-
{
1156-
m_lhs.to_begin();
1157-
m_rhs.to_begin();
1158-
step_to_index();
1159-
}
1160-
else
1161-
{
1162-
m_rhs.to_begin();
1163-
for (size_type i = 0; i < m_index.size(); ++i)
1164-
{
1165-
m_rhs.step(i + a.m_step_dim, m_index[i]);
1166-
}
1167-
}
1168-
}
1169-
1170-
template <class E1, class E2>
1171-
inline void strided_loop_assigner<E1, E2>::cursor::step_to_index()
1172-
{
1173-
size_type step_dim = p_assigner->m_step_dim;
1174-
for (size_type i = 0; i < m_index.size(); ++i)
1175-
{
1176-
m_lhs.step(i + step_dim, m_index[i]);
1177-
m_rhs.step(i + step_dim, m_index[i]);
1178-
}
1179-
}
11801138
}

‎include/xtensor/core/xsemantic.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ namespace xt
526526
}
527527
else
528528
{
529-
using new_policy = assign_policy<noalias_tag, P::broadcasting, P::parallelism>;
529+
using new_policy = assign_policy<noalias_tag, typename P::broadcasting, typename P::parallelism>;
530530
return this->derived_cast().assign_xexpression(e, new_policy{});
531531
}
532532
#endif

0 commit comments

Comments
 (0)