I will admit up front that I have little experience with SIMD-heavy code and I'm really just coming at this as someone with a sense for Swift aesthetics. There may well be compelling reasons why some of my suggestions are wrong.
- What is your evaluation of the proposal?
I think this is a good addition to the language, and the proposal does a good job identifying the scope and solving many thorny questions. However, I have many quibbles with the specific design selected.
- Is the problem being addressed significant enough to warrant a change to Swift?
Yes. We want some good solutions to these problems, and SIMD instructions are so low-level that they really need to be at least partially in the standard library.
- Does this proposal fit well with the feel and direction of Swift?
This is where I disagree with the proposal. Except at the broadest, most conceptual levels, I think it doesn't really match the feel and direction of Swift.
Mixed metaphors
Vectors are in some ways collection-like—they have several elements of identical type, they are subscriptable, they can be initialized from an array literal, etc. And yet they don't conform to Collection
, apparently out of concern that users may accidentally write un-vectorized algorithms with that conformance.
Vectors are in other ways tuple-like—they have a size fixed at compile time, the elements don't (typically) have the same semantic meaning, and they aren't supposed to be processed by loops. And yet their elements are accessed through a dynamic, potentially computed subscript instead of tuple.0
-style properties and they can't be initialized by tuple literals; as a result, the compiler can't statically diagnose invalid literals or accesses.
I would like to see a vector design which knows what it's trying to be and more thoroughly embraces one or the other of these precedents. If some parts of its design can't be achieved in Swift 5, the remaining plans should at least be outlined in a "future directions" section.
That's not to say we should blindly copy everything about collections or everything about tuples—a vector isn't an ordinary collection and it isn't an ordinary tuple—but we should consciously try to move the design closer to one of them.
Non-generic
I strongly prefer the VectorN<Element>
design for several reasons.
The first is simply that I believe it will feel more natural. I can't think of another case where we generate types in bulk rather than using a generic representation that would be easily achievable in our current type system. Generics exist specifically to allow types to be composed; why not use them?
The second is surface area. The way the proposal is written obscures the fact that it introduces something like 92 new public types. 53 of those types could be collapsed to something like 28 (assuming matching VectorN<Element>
structs and VectorizableN
, VectorizableIntegerN
, and VectorizableFloatingPointN
protocols) by adopting generics.
The third is extensibility. It would be nice to be able to say things like Vector3<CGFloat>
, but the current design makes that very difficult. I think we could design some sort of forwarding system into the Vectorizable
protocols so that types outside the standard library could be elements of vectors, so long as they can be losslessly converted into an underlying vectorizable type.
The fourth is the inherent GYB-biness of the proposal. By generating so many unrelated types sprinkled throughout the type system, we lose the opportunity to improve the implementation of the SIMD system over time. For instance, it's not inconceivable that a future version of Swift with generic integer literal parameters could rework builtins to support generic parameters—think Builtin.fadd<Builtin.Float32, 4>()
instead of Builtin.fadd_Vec4xFloat32()
. This would allow the code to be massively—perhaps even completely—de-GYBbed, but if we're generating tons of types all over the place, our gains from this will be pretty small.
In previous discussions, a common counter-argument has been that the implementations of these protocols will require just as much generated code. While this is probably true, I think it's irrelevant. What matters more is the public surface area of the feature, and that will most likely be reduced by this change.
Namespacing
I'm not entirely convinced that these types should be in the core standard library (i.e. libswiftCore), rather than in the simd
module or something similar. They are somewhat specialized, and although I wouldn't expect anyone to use the name "vector" for a resizable array (since we use Array
for that meaning), I could see someone wanting to use "vector" for the mathematical object without wanting the semantics of our implementations.
I don't understand why some types are prefixed with SIMD
and others are not. The choice of naming seems somewhat random. If these types are included in the core standard library, I think they should all be prefixed with SIMD
; if they're in a separate simd
module, I think few or none of them should be.
The SIMD.Mask
types
If I understand correctly, VectorN<Bool>
can't really work for predicates/masks because the underlying representation of these is machine-dependent and isn't always compatible with a one-bit boolean. However, I don't understand why we can't model these as, say, VectorN<VectorBool>
or something similar. I also have a sneaking suspicion (though I haven't tried it) that a good generic design could adjust for the fact that Bool
looks different when it's inside a vector.
What's good
This is a long list of complaints, so I want to also stress some of what this proposal gets right:
-
The decision to use distinct VectorN
types, and the sizes chosen.
-
The operators available, and particularly the final decisions on comparisons and logical operators.
-
The decision to break precedent with the naming of replacing(mask:with:)
(or whatever it is eventually called).
-
The extremely low-overhead design.
I hate to suggest this many revisions during review, but the previous stage of discussion seemed somewhat abbreviated.
- If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
As I said at the beginning, I have little personal experience with SIMD code.
- How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
Mildly in-depth? I have prototyped some of the changes I discuss, although the prototype was never completed.