Minimize checking in subspan (#569)

* rewrite span subspan checks to help optimizations
* Removed checking pointer for null for subspans. We would never
  check pointer for null in between ptr and ptr+size in the
  original span, so there seems to be  no reason to do so for
  subspans, provided that the subspan's boundaries are ensured
  to be within the range of the original span's boundaries.

This change allows to simplify generated code, for example, to
remove 5 out of 9 branches in code generated from the following
by MSVC, and 4 out 8 branches in clang and gcc-generated code:

span<int> mysubspan(int* p, std::ptrdiff_t size, std::ptrdiff_t i)
{
  if (p != nullptr)
  {
    span<int> s = { p, size };
    return s.subspan(i);
  }

  return { nullptr };
}

Similar effects are achieved for dynamic subspans of static spans,
where in the following code we remove 2 out of 4 branchs in MSVC
and GCC-generated code:

int test_dynamic_subspan_of_static_span(std::ptrdiff_t i)
{
  int x[] = { 0,1,2,3,4,5 };
  span<int, 6> s = { x };
  auto subspan = s.subspan(i);
  return subspan.size();
}
This commit is contained in:
Anna Gringauze 2017-11-03 16:13:39 -07:00 committed by Neil MacIntosh
parent 4862a270b9
commit 9a7897915e

View File

@ -443,15 +443,16 @@ public:
template <std::ptrdiff_t Count> template <std::ptrdiff_t Count>
constexpr span<element_type, Count> last() const constexpr span<element_type, Count> last() const
{ {
Expects(Count >= 0 && Count <= size()); Expects(Count >= 0 && size() - Count >= 0);
return {data() + (size() - Count), Count}; return {data() + (size() - Count), Count};
} }
template <std::ptrdiff_t Offset, std::ptrdiff_t Count = dynamic_extent> template <std::ptrdiff_t Offset, std::ptrdiff_t Count = dynamic_extent>
constexpr span<element_type, Count> subspan() const constexpr span<element_type, Count> subspan() const
{ {
Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && Expects((Offset >= 0 && size() - Offset >= 0) &&
(Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size())));
return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
} }
@ -463,18 +464,16 @@ public:
constexpr span<element_type, dynamic_extent> last(index_type count) const constexpr span<element_type, dynamic_extent> last(index_type count) const
{ {
Expects(count >= 0 && count <= size()); return make_subspan(size() - count, dynamic_extent, subspan_selector<Extent>{});
return {data() + (size() - count), count};
} }
constexpr span<element_type, dynamic_extent> subspan(index_type offset, constexpr span<element_type, dynamic_extent> subspan(index_type offset,
index_type count = dynamic_extent) const index_type count = dynamic_extent) const
{ {
Expects((offset == 0 || (offset > 0 && offset <= size())) && return make_subspan(offset, count, subspan_selector<Extent>{});
(count == dynamic_extent || (count >= 0 && offset + count <= size())));
return {data() + offset, count == dynamic_extent ? size() - offset : count};
} }
// [span.obs], span observers // [span.obs], span observers
constexpr index_type length() const GSL_NOEXCEPT { return size(); } constexpr index_type length() const GSL_NOEXCEPT { return size(); }
constexpr index_type size() const GSL_NOEXCEPT { return storage_.size(); } constexpr index_type size() const GSL_NOEXCEPT { return storage_.size(); }
@ -517,10 +516,13 @@ private:
class storage_type : public ExtentType class storage_type : public ExtentType
{ {
public: public:
// checked parameter is needed to remove unnecessary null check in subspans
template <class OtherExtentType> template <class OtherExtentType>
constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) constexpr storage_type(pointer data, OtherExtentType ext, bool checked = false) : ExtentType(ext), data_(data)
{ {
Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); Expects(((checked || !data) && ExtentType::size() == 0) ||
((checked || data) && ExtentType::size() >= 0));
} }
constexpr pointer data() const GSL_NOEXCEPT { return data_; } constexpr pointer data() const GSL_NOEXCEPT { return data_; }
@ -530,8 +532,38 @@ private:
}; };
storage_type<details::extent_type<Extent>> storage_; storage_type<details::extent_type<Extent>> storage_;
// The rest is needed to remove unnecessary null check in subspans
constexpr span(pointer ptr, index_type count, bool checked) : storage_(ptr, count, checked) {}
template <std::ptrdiff_t CallerExtent>
class subspan_selector {};
template <std::ptrdiff_t CallerExtent>
span<element_type, dynamic_extent> make_subspan(index_type offset,
index_type count,
subspan_selector<CallerExtent>) const GSL_NOEXCEPT
{
span<element_type, dynamic_extent> tmp(*this);
return tmp.subspan(offset, count);
}
span<element_type, dynamic_extent> make_subspan(index_type offset,
index_type count,
subspan_selector<dynamic_extent>) const GSL_NOEXCEPT
{
Expects(offset >= 0 && size() - offset >= 0);
if (count == dynamic_extent)
{
return { data() + offset, size() - offset, true };
}
Expects(count >= 0 && size() - offset >= count);
return { data() + offset, count, true };
}
}; };
// [span.comparison], span comparison operators // [span.comparison], span comparison operators
template <class ElementType, std::ptrdiff_t FirstExtent, std::ptrdiff_t SecondExtent> template <class ElementType, std::ptrdiff_t FirstExtent, std::ptrdiff_t SecondExtent>
inline constexpr bool operator==(const span<ElementType, FirstExtent>& l, inline constexpr bool operator==(const span<ElementType, FirstExtent>& l,