On 19/04/13 00:29, Marc Glisse wrote:
On Thu, 18 Apr 2013, Mathias Gaunard wrote:
Development of Boost.SIMD will still proceed, aiming for integration in Boost, but standardization appears to be definitely out of the question. Any feedback of the API presented in the proposal is welcome. http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3571.pdf
Copying here my earlier comments so they are in the same place as others'. Some of them are only relevant for standardization, not for a boost library.
I wasn't subscribed to c++-lib-ext at the time, so I missed them.
Hello,
a few comments while reading N3571.
pack
seems more similar to std::array than std::tuple to me.
It's statically-sized, so both runtime and compile-time access are possible. Compile-time access could be significantly more efficient.
We could even dream of merging pack and array into a single type.
I don't think that's a good idea, pack has a very strong numerical
semantic. You don't really want to do +, * or / on arrays.
Plus pack
As much as possible, I would like to avoid having a different interface for vectors and scalars. We have std::min for scalars, we can overload it for vectors instead of having simd::min.
The idea is that any SIMD code should be valid scalar code as well. I'm not sure whether we want the selection for functions to use ADL or whether it should be std::min. I have no strong opinion on this.
We have ?: for scalars, you don't need to restrict yourself to a pure library
Overloading ?: would probably have to be a standalone language extension to core no?
Masking: it is a bit strange to be able to do pack<double> & int but not double & int. Currently in gcc we require that you (reinterpret) cast the pack<double> to a pack<some integer>, do the masking and go back.
It was a wish from my colleague; I personally think it might be better to align the pack operators to be the same as the scalar equivalents, and therefore not allow it without a cast.
Any policy on reinterpret_cast-ing a pack to a pack of a different type?
In Boost.SIMD there is a bitwise_cast<To>(from) function, which is essentially the same as To to; memcpy(&to, &from, sizeof(from)); return to; This can be optimized to a reinterpret_cast in some cases, but reinterpret_cast itself is dangerous because of aliasing issues. ass T , std :: size_t N = unspecified >
struct alignas ( sizeof ( T ) * N ) pack
Do you really want to specify that large an alignment? You give examples with N=100...
N can never be 100, it's a power of 2. It would be possible to relax the alignment requirement somewhat however.
Maybe operator[] const could return by value if it wants to?
Yes.
Since the splat constructor is implicit, you may not need to document all the mixed operations.
It was to make things clearer, but I guess it might not be necessary.
Any notion of a subvector?
No, though it would probably be useful to be able to split any vector in two. Duly noted.
For gather and others, the proposal accepts mixing vector sizes.
For scatter/gather, it doesn't accept a number of indices different than the size of the vectors being loaded. Conversions are allowed to happen however, you can load from a uint8* to a vector of int32.
cmath functions: it is not clear what signatures are supported, in particular for functions that use several types (ldexp has double and int). The list doesn't seem to exactly match cmath, actually. frexp takes an int*, does the vector version take a pointer to a vector, or some scatter-like vector?
We have some equivalents in Boost.SIMD, to make things simpler we use int32 for float and int64 for double, so that the sizes of vectors are the same.
Traits: are those supposed to be template aliases? Or to derive from what they are supposed to "return"? Or have a typedef ... type; inside?
They're metafunctions, i.e. classes with a type member typedef.
For transform and accumulate, I have seen other proposals that specify new versions more in terms of permissions (what the compiler is allowed to do) and less implementation. Depending on the tag argument you pass to transform/accumulate, you give the compiler permission to reorder the operations, or do other transformations and it then deduces that it can parallelize and/or vectorize. Looks nice. Note that it doesn't contradict this proposal, simd::transform can always forward to std::transform(..., vectorizable_tag()) (or in the reverse direction).
simd::transform takes a function object that must be valid with both scalar and pack values; std::transform only requires that the function object be valid with scalar values. That's the main difference between the two functions.