ornl / cpp-proposals-pub Goto Github PK
View Code? Open in Web Editor NEWCollaborating on papers for the ISO C++ committee - public repo
Collaborating on papers for the ISO C++ committee - public repo
Per request by @hcedwar, I'm posting the link to the minutes from Saturday LWG small group P0009R8 (mdspan): http://wiki.edg.com/bin/view/Wg21sandiego2018/SanDiego2018P0009
Per Jens' comment:
I’m still not seeing a discussion (in the “motivation” section or similar) why std::atomic<T&> can’t be made to work and why rewriting the specification of std::atomic<> in terms of std::atomic_ref<> was not performed. This seems crucial information for posterity.
The appendices to P0009 discuss a number of alternatives/enhancements that could be made to P0009 if other proposals successfully get through the committee.
At this point, given that P0009 is close to advancing to a plenary vote, we should remove these appendices. None of the things in the appendix are actually part of the proposal. That might make you think that they are harmless to include. However, the fact that they are in the proposal may confuse people, especially committee members who are unfamiliar with the proposal and may skim it instead of reading it entirely.
We do not want people to raise objections to P0009 in plenary because they looked through the paper, saw something that they disliked in the appendices, and assumed it was part of the proposal.
A list to prior/related papers should be sufficient.
The code formatting is very inconsistent right now. It all should follow the ISO coding guidelines.
For example:
template< ptrdiff_t ... LHS , ptrdiff_t ... RHS >
constexpr bool operator == ( extents<LHS...> const & lhs , extents<RHS...> const & rhs ) ;
The above code should be:
template <ptrdiff_t... LHS , ptrdiff_t... RHS>
constexpr bool operator==(const extents<LHS...>& lhs, const extents<RHS...>& rhs);
Action Items:
struct layout_stride {
template <typename Extents>
class mapping {
public:
constexpr mapping() noexcept;
constexpr mapping(mapping const& other) noexcept;
constexpr mapping(mapping&& other) noexcept;
constexpr mapping(Extents e, array<ptrdiff_t, Extents::rank()> s) noexcept;
template<class OtherExtents>
constexpr mapping(const mapping<OtherExtents>& other);
mapping& operator=() noexcept = default;
mapping& operator=(mapping const& other) noexcept = default;
template<class OtherExtents>
constexpr mapping& operator=(const mapping<OtherExtents>& other);
Extents get_extents() const noexcept;
array<ptrdiff_t, Extents::rank()> get_strides() const noexcept;
constexpr typename Extents::index_type required_span_size() const noexcept;
template <class... Indices>
typename Extents::index_type operator()(Indices... is) const;
static constexpr bool is_always_unique() noexcept;
static constexpr bool is_always_contiguous() noexcept;
static constexpr bool is_always_strided() noexcept;
constexpr bool is_unique() const noexcept;
constexpr bool is_contiguous() const noexcept;
constexpr bool is_strided() const noexcept;
ptrdiff_t stride(size_t rank) const noexcept;
private:
Extents extents_; // exposition only
//TODO: should this be extents<dynamic_extent,...,dynamic_extent> ?
array<ptrdiff_t, Extents::rank()> strides_; // exposition only
};
};
constexpr mapping() noexcept;
get_extents() == Extents()
and get_strides() == array<ptrdiff_t, Extents::rank()>()
constexpr mapping(mapping const& other) noexcept;
get_extents() == other.get_extents()
and get_strides() == other.get_strides()
constexpr mapping(mapping&& other) noexcept;
move(other.extents_)
.get_extents()
returns a copy of an Extents that is equal to the copy returned by other.get_extents()
before the invocation of the move and get_strides()
returns a copy of an array<ptrdiff_t,Extents::rank()>
that is equal to the copy returned by other.get_strides()
before the invocation of the moveconstexpr mapping(Extents e, array<ptrdiff_t, Extents::rank()> s) noexcept;
0,...,Extents::rank()-1
o(i)
with 0 <= i < Extents::rank()
such that stride(o(i)) >= stride(o(i-1))*get_extent(o(i-1))
for 1 <= i < Extents::rank()
get_extents() == e
and get_strides() == s
.template<class OtherExtents>
constexpr mapping(const mapping<OtherExtents>& other);
get_extents() == other.get_extents()
and get_strides() == other.get_strides()
.Extents get_extents() const noexcept;
Extents get_strides() const noexcept;
typename Extents::index_type required_span_size() const noexcept;
get_extents().extent(r) * stride(r)
for all r
where 0 <= r < get_extents().rank()
template <class... Indices>
typename Extents::index_type operator()(Indices... i) const noexcept;
i...
is i0, i1, i2,..., ik
(where k == Extents::rank() - 1
) and s = get_strides()
, returns i0*s[1] + i1*s[2] +...+ ik*s[k]
sizeof...(Indices) == Extents::rank()
,is_convertible_v<Indices, typename Extents::index_type> && ...
static constexpr bool is_always_unique() noexcept;
static constexpr bool is_always_strided() noexcept;
constexpr bool is_unique() const noexcept;
constexpr bool is_strided() const noexcept;
TODO: should this be always unique?
static constexpr bool is_always_contiguous() noexcept;
constexpr bool is_contiguous() const noexcept;
0,...,Extents::rank()-1
o(i)
with 0 <= i < Extents::rank()
such that min(stride(o(i)) == 1
and stride(o(i)) == stride(o(i-1))*get_extent(o(i-1))
with 1 <= i < Extents::rank()
otherwise returns falseindex_type stride(int r) const noexcept;
strides_(r)
constexpr bool operator==(const mapping& other) const noexcept;
(get_extents() == other.get_extents()) && (get_strides() == other.get_strides())
constexpr bool operator!=(const mapping& other) const noexcept;
(get_extents() != other.get_extents()) || (get_strides() != other.get_strides())
The layout_stride is_contiguous
covers the conditions for contiguous property.
These properties do not need to be enforced / duplicated in the constructor.
A stride of zero should be allowed in the constructor.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1161r0.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1277r0.html
This proposes language changes which will open the door to having an_mdspan[i,j,k]
work.
I am strongly in favour of this.
Not quite done yet:
// [mdspan.subspan], subspan creation
template<class ElementType, class Extents, class LayoutPolicy,
class Accessor, class... SliceSpecifiers>
basic_mdspan<ElementType, E /* see-below */, layout_stride, Accessor>
subspan(const basic_mdspan<ElementType, Extents, LayoutPolicy, Accessor>& src, SliceSpecifiers ... slices) noexcept;
basic_mdspan
that is a view on a (potentially trivial) subset of another basic_mdspan
.extents
.C
be the number of parameters in SliceSpecifiers
which are convertible to ptrdiff_t
ranges[r]
denote the rth value in the parameter pack slices, which is not convertible to ptrdiff_t
sub
be the return value of subspan
first[r]
denote the rth lower bound of slices[r]
.
slices[r]
is convertible to ptrdiff_t
first[r]==slices[r]
slices[r]
is convertible to pair<ptrdiff_t,ptrdiff_t>
first[r]==pair<ptrdiff_t,ptrdiff_t>(slices[r]).first()
slices[r]
is convertible to alltype_t
first[r]==0
last[r]
denote the rth upper bound of slices[r]
.
slices[r]
is convertible to ptrdiff_t
last[r]==slices[r]+1
slices[r]
is convertible to pair<ptrdiff_t,ptrdiff_t>
last[r]==pair<ptrdiff_t,ptrdiff_t>(slices[r]).second()
slices[r]
is convertible to alltype_t
last[r]==src.extent(r)
sizeof(slices...)==Extents::rank()
slices[r]
is convertible to ptrdiff_t
, pair<ptrdiff_t,ptrdiff_t>
or alltype_t
0<=first[r]<src.extent(r)
0<last[r]<=src.extent(r)
LayoutPolicy
is layout_right
, layout_left
, or layout_stride
E::rank() == Extents::rank()-C
ranges[r]
is the nth value of ranges
convertible to alltype_t
or pair<ptrdiff_t,ptrdiff_t>
and slices[k]
is the n
th value of slices
convertible to all type_t
or pair<ptrdiff_t,ptrdiff_t>
then sub.extent(r)==last[k]-first[k]
and sub.stride(r)==src.stride(k)
sub.ptr_==src.ptr_ + first[0]*src.stride(0) + ... + first[Extents::rank()-1]*src.stride(Extents::rank()-1)
typedef extents<100,dynamic_extent,3> Extents3D;
layout_right::mapping<Extents3D> map_right(
basic_mdspan<int,Extents3D,layout_right> a()
P0454r0 titled "Proposed wording, based on span P0122R] for an mdspan with a core subset of the full mdspan functionality" emphasized span and mdspan interoperability. The version 1 revision titled "Proposed wording for mdspan" drops the P0454r0 span-alignment scope, predominantly pulls significant content from (p0009)[https://github.com/kokkos/array_ref/blob/master/proposals/P0009.rst], and drops numerous P0009 authors some of whom were integral to the years of design-investment in mdspan, without even citing P0009.
I am playing with the implementation from https://github.com/hcedwar/ornl-cpp-proposals-pub/blob/master/P0009/prototype/include/mdspan
It works for me with mdspan
but I cannot use subspan()
.
It is not trivial to understand why.
Here is the example from D0009r8 followed by an example from me.
#include <iostream>
#include <mdspan>
using namespace std::experimental::fundamentals_v3;
int main(int argc, char *argv[]) {
/* Example from D0009r8 */
// Create a mapping
typedef extents<3,dynamic_extent,7> Extents3D;
layout_right::template mapping<Extents3D> map_right(10);
// Allocate a basic_mdspan
int* ptr = new int[3*8*10];
basic_mdspan<int,Extents3D,layout_right> a(ptr,map_right);
// Initialize the span
for(int i0=0; i0<a.extent(0); i0++)
for(int i1=0; i1<a.extent(1); i1++)
for(int i2=0; i2<a.extent(2); i2++)
a(i0,i1,i2) = 10000*i0+100*i1+i2;
// Create Subspan
auto a_sub = subspan(a,1,std::pair<int,int>(4,6),std::pair<int,int>(1,6));
// Print values of subspan
for(int i0=0; i0<a_sub.extent(0); i0++) {
for(int i1=0; i1<a_sub.extent(1); i1++)
std::cout << a_sub(i0,i1) << " ";
std::cout << std::endl;
}
/* Output
10401 10402 10403 10404 10405
10501 10502 10503 10504 10505
*/
/* Some other example */
using space = mdspan<double, 10, 20, 30>;
double a[10*20*30];
const space s { a };
auto t = subspan(s, all, all, all);
auto u = subspan(s, 1, 2, 3);
}
My notes:
mdspan LWG review
Daniel Krugler of LWG will help us polish according to LWG expectations
usage of ‘span’ requires reference to C++20 working document
need every section to have stable name
namespace for the TS – std::experimental::fundamentals_v3
before header synopsis a sentence of overview
exposition only in italics k
input iterators has used the term ‘domain’
structure domain and codomain as definitions for this clause in italics; email to the library reflector before the pre-meeting mailing for discussion
LEWG ok with ‘all’ and ‘all_type’
Destructor does not have noexcept
‘noexecept’ typo
typename template layout::mapping
templated member functions “OtherElementType” “OtherLayoutType” …
assignment conversion operator should not be variadic
“DynamicExtent” argument should be “dynamic_extent”
All function arguments in the synopsis are named
Exposition only members are in italics and each is marked with comment ‘exposition only’
perhaps another exposition name than ‘map’
section breaks within synopsis need to have stable names for the subsections, do not have colons
3.2.1
template arguments stated as requirements
“ElementType” shall be …
“Extents” shall be a specialization of extents.
See the algorithm section for templates as policy requirements; policy requirements appear before the synopsis
3.2.2
Cartesian product times sign need to be math font times, put formula in a separate line
Use natural number symbol for extent definition: N_0×N_1 … etc
Omit “(at compile time)” and “(at runtime)”
Ellipses should not be in code font
Marshall – would like an example – would be helpful
Function signature must match the synopsis
Clause for returns should not cover requires clause, add throws nothing
Return for static_extent If rank() <= r then 1
Change all this to “effects equivalent to returns”
3.3 extents
Should be before mdpsan
Destructor after the constructor
… carry Cartesian description to this
Synopsis to state what the class is expected to store – the dynamically sized extents
Would be simpler to describe with exposition only array member of all extents with note that implementation is expected to only store dynamic extents
Template “ExtentsSpecification” name is clearer
Default constructor initializes stored dynamic extents as zero
See optional or variant or span for constructor argument template description / requirements
rank() : returns sizeof…(Extents)
rank_dynamic() : returns number of Extents that are equal to dynamic_extent
static_extent(int i) : if ith < rank() returns the ith element of Extents… ; otherwise 1
throws nothing
constructor needs wording
Shall not participate in overload resolution unless sizeof…(dynamic_extents) == rank_dynamic()
Wording for constructor with arguments talk about input type
Look at tuple equality comparison
3.2.3 Codomain observers
Use math style for i^th
“dynanic” typo
“A span that refers to the codomain”
Ill-formed if requires clauses not satisfied
Use fold expression for IndexType requirement
Wording to prevent “bool” from being used
3.5 Layout Policy Requirements
Split into requirements and specific types
Use “specialized” not “instantiated”
Describe without mentioning basic_mdspan
3.5.2 Concept
Look at allocator and container for table of valid expressions; e.g., Table 77
Preamble to table also a table that identifies symbols
Exposition only layout::mapping<…>::extent ? but very hard to do.
Suggestion – layout::mapping::extents() instead of duplicating ?
Must include the extents-based constructor
3.2.6 has the wording that belongs here
“contiguous” – steal wording from contiguous iterators, remove term ‘span’, no holes in the codomain
Bijective is non-normative note
Wording for strided – true if and only iff …conditions… ; could lead in with “let” …
Wording only refers to extents, layout, mapping
is__strided = / see below */
stride( int i )
returns the distance between codomain when incrementing one coordinate in the domain
0 <= i < rank()
3.2.4 Constructors…
Default constructor is post_condition
Assignment
3.6 Accessor
pointer is a contiguous iterator
need to say what ‘T’ is
An accessor shall meet the requirements …
‘p’ denotes a pointer to a type T
3.5.1 Predefined standard layout policies
This is not in wording !
Preamble “These predefined layout policies are provided…”
Just show code
“row major” and “column major” are notes
Look at container requirements and container for requirements and how they satisfy the layout requirements
TARGET post-meeting
FYI: There is a tensor interface standardization effort in progress:
https://github.com/MolSSI/tensor-interfaces
I created an FYI issue on their repository pointing to our ISO/C++ paper.
Accessor abstraction discussion as manifested in PR #55
Current PR #55 re-conceives the accessor abstraction a something similar to an array of unknown bound when it is used as a function parameter. It specifically avoids redefining pointer and only defines the reference type returned by the subscripting operator.
In the synopsis of P0009:
template< ptrdiff_t ... LHS , ptrdiff_t ... RHS >
constexpr bool operator == ( extents<LHS...> const & lhs , extents<RHS...> const & rhs ) ;
template< ptrdiff_t ... LHS , ptrdiff_t ... RHS >
constexpr bool operator != ( extents<LHS...> const & lhs , extents<RHS...> const & rhs ) ;
These functions should be noexcept.
The specific layouts define required_span_size()
as the product of extents().extent(r)
for all r
where 0 <= r
< extents().rank()
, but the requirements table defines it as 1 plus the maximum value of m(i...)
. These definitions contradict for a rank-0 mdspan
, or for a degeneratemdspan
. "Degenerate" means that the mdspan
has nonzero rank, but one or more dimensions is zero.
Kokkos just defines the span()
as the product of the dimensions. Thus, I propose the following fix for the definition of required_span_size
in the table:
* *Returns:*
* If the codomain has size zero, then zero;
* Otherwise, 1 plus the maximum value of `m(i...)`.
It looks like the current proposal does not have the usual a[j][j][k]
syntax.
Since it cannot be an omission :-) , what are the reasons for this absence?
It is available in std::valarray
, https://www.boost.org/doc/libs/release/libs/multi_array/ and so on.
Since I have not attended many recent ISO C++ meetings about array_ref
or mdspan
, I do not remember this discussion.
If I understand the good honey pot of a(i,j,k)
syntax for Fortran programmers, it seems strange or repulsive for C/C++ programmers. It also prevents writing generic code taking either a multidimensional C++ array or a mdspan
.
When writing standard library wording, ranges need to be expressed using range syntax. For example:
NO (don't do this): 0 <= i < rank_dynamic()
.
YES (do this): i
in [0, rank_dynamic())
.
Incorporate the following from P0454r1:
AccessorPolicy
concept.mdspan
to basic_mdspan
.mdspan
alias to basic_mdspan
.Need to talk to Bryce about his accessor idea.
This is with Bryce right now.
basic_mdspan constructors that take const x & parameters in P0009r8, take only const x in the reference implementation.
cpp-proposals-pub/P0009/P0009.bs
Line 1759 in 9bbb0dd
Bikeshed complains about those lines:
$ bikeshed spec P0009.bs
FATAL ERROR: Line 1758 isn't indented enough (needs 1 indent) to be valid Markdown:
"<xmp class='language-c++'"
✘ Did not generate, due to fatal errors
When I delete the code example, Bikeshed runs just fine. Also, the example looks incomplete and not really related to the stuff above it, as if some lines got deleted.
In the current paper, we have the following lockfree property macros:
// 3.� lock-free property
#define ATOMIC_REF_BOOL_LOCK_FREE unspecified
#define ATOMIC_REF_CHAR_LOCK_FREE unspecified
#define ATOMIC_REF_CHAR16_T_LOCK_FREE unspecified
#define ATOMIC_REF_CHAR32_T_LOCK_FREE unspecified
#define ATOMIC_REF_WCHAR_T_LOCK_FREE unspecified
#define ATOMIC_REF_SHORT_LOCK_FREE unspecified
#define ATOMIC_REF_INT_LOCK_FREE unspecified
#define ATOMIC_REF_LONG_LOCK_FREE unspecified
#define ATOMIC_REF_LLONG_LOCK_FREE unspecified
#define ATOMIC_REF_POINTER_LOCK_FREE unspecified
These appear to be similar to atomic<T>
s lockfree property macros, which are designed to interoperate with C atomics.
I'm not sure that we need these for atomic_ref. What their role be? C++ users will determine lockfreeness via the member function/trait. Do we need these for C compatibility? Will C be able to interact/interoperate with atomic_ref?
Reference implementation strengthened with a little extensibility testing in test subdirectory
David working on this with the proposal originating from Facebook
The paper currently has the following note next to subspan:
Note: it is quality of implementation whether static extents are preserved if possible.
I doubt this will fly since the type of the return value is part of the ABI, and thus introduces an ABI dependence on QOI. We should probably remove this and specify the requirements for static extent preservation.
It should either be index_type
or some new nested type member rank_type
. See P0454 for examples.
Requiring the definition of nested type aliases in a concept is annoying, because you can't provide a "default" definition, so type authors implementing your concept must define the nested type aliases. This pattern exists mostly for historical reasons:
decltype
, etc.typename
(e.g. typename X<T>::type
instead of X_t<T>
).Iterators, containers, and allocators all suffer from this problem.
In modern C++, there's a better solution: define a traits class for each nested type aliases (not a single traits class for all the types, like iterator_traits
or numeric_traits
), provide a reasonable default, and allow users to customize it. For example:
template <typename MyConcept>
struct X { using type = /* default */; };
template <typename MyConcept>
using X_t = typename X<T>::type;
The executors proposal uses this pattern (executor_future_t
, executor_index_t
, etc). We should do the same for mdspan concepts like Accessor
and Layout
.
Might be a simple markdown mistake, but seems to be consistently missing
We should consider asking for std::dyn
; std::dynamic_extent
is unnecessarily verbose.
static constexpr
size_t is_always_lockfree;
type should be boolatomic
versionsrequired_alignment
note, eliminate may and refer to hardwarerequired_alignment
*ptr
to name the referenced objectnoexcept
load
wording missing return type*ptr
exchange
wording needs return typeexchange
wording remove note because it refers to a deleted exampleatomic
wordingatomic
first
, last
, and R
is not valid source code, because it uses a run-time value r
to get the r
-th entry of a parameter pack slices...
.R
needs to be constexpr
, because Ensures needs the R[k]
-th entry of a parameter pack slices...
.R
, since we use that elsewhere in the document as the rank (an integer value, not an array).I think we could define R
with math instead of code. We can say in a Note that we know its length and entries at compile time. This would make it easier to define first
and last
in code.
What is the status of C-style array syntax for extents T[M][N]
for mdspan?
Is it still 'preferred'?
At some point around r5 of P0009 this 'preferred' natural syntax for extents was bumped to an appendix and to P0332 for proposals to relax language constraints on incomplete bounds. Then, for the last few revisions, I don't see a reference.
Thanks for any pointers - forum discussions or elsewhere in github issues perhaps.
This will help describe type requirements involving extents. E.g. we can just say the requirement is is_extents_v<Extents> == true
.
Look to the SIMD types paper/Parallelism TS v2 for examples of how this might be worded.
Take everything from the Atomic section in current working draft (post Jacksonville) and make a new atomic_ref section.
Note: As far as I can tell, the issues on this repo are a de facto place for national labs coordination on C++ standards work and discussion. If we come up with a better place, we should move this there (maybe a Slack or something?).
Since one of the DOE labs is a primary author on the (relatively massive) Future
concept design effort (P0154r0), it would be nice to get some early feedback from the national labs on this proposal. We can put labs-related concerns on this issue directly and I can relay them to the proposal repository (https://github.com/executors/futures), or you can open issues on that repository directly.
Right now the synopsis goes:
struct mdspan
{
// Usual ctors.
// Other functions.
// Converting ctors.
};
Likewise for the sections describing the member functions themselves.
All the constructors should be grouped together and before the other member functions.
David working on adding a std-executors backend for Kokkos to report back to the proposal folks.
The current C++ draft defines span
as a "view." P0009 also defines mdspan
as a "view." I don't see a clear definition of "view" anywhere in the draft, even with string_view
. I understand what "view" means -- something that accesses something else without owning it -- but I wonder if the Standard needs to define some concepts, in the same way that it defines various concepts for the Container section.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.