# SIMD additions

**URL:** <https://forums.swift.org/t/simd-additions/21489>\
**Category:** Pitches\
**Tags:** numerics\
**Created:** [March 12, 2019, 5:44am UTC](https://forums.swift.org/t/simd-additions/21489 "2019-03-12T05:44:30Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [March 12, 2019, 5:44am UTC](https://forums.swift.org/t/simd-additions/21489/1 "2019-03-12T05:44:30Z")

</div>

Hi all --

There are a few features that we pushed out of the previous SIMD proposal in the interests of time, as well as a few features that have been requested by internal Apple adopters of Swift as they've started using the new SIMD APIs.

A first draft of the pitch is here: [https://github.com/stephentyrone/swift-evolution/blob/simd-additions/proposals/0000-simd-additions.md](https://github.com/stephentyrone/swift-evolution/blob/simd-additions/proposals/0000-simd-additions.md). Note that there's (as of now) no ABI stability, etc sections in the pitch. These are all purely additive changes to the library.

Looking forward to everyone's thoughts.

---

<div class="post-metadata">

**Author:** ![Torust](https://avatars.discourse-cdn.com/v4/letter/t/f17d59/32.png) [@Torust](https://forums.swift.org/u/Torust)\
**Post date:** [March 12, 2019, 7:55am UTC](https://forums.swift.org/t/simd-additions/21489/2 "2019-03-12T07:55:39Z")

</div>

```swift
SIMD3 init(_ xy: SIMD2<Scalar>, _ z: Scalar)
SIMD4.init(_ xyz: SIMD3<Scalar>, _ w: Scalar)

```

I already provide extensions for this myself; definitely +1 to having them in the library.

* * *

`SIMD.min(), SIMD.max(), SIMD.sum(), any(), all()`

Again, I already provide these. +1.

* * *

For loading and storing from collections: I feel like this is better written as `init(collection[start...])` rather than having `start` as a separate argument. This could maybe go with `SIMD.replacePrefix(of collection: inout C)`, where `C` can also be sliced.

* * *

`indexOfMinValue, indexOfMaxValue`:

`minComponentIndex` or `minScalarIndex` maybe?

* * *

Swizzles: at the moment, I have this implemented through a giant auto-generated file that provides computed getters/setters. Having a nicer solution would be great.

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [March 17, 2019, 4:05am UTC](https://forums.swift.org/t/simd-additions/21489/3 "2019-03-17T04:05:32Z")

</div>

Hi Steve,

Here are my thoughts for whatever they are worth. I am not a numerics expert 🙂. Also + @rxwei for visibility:

- I'm generally very +1 on this, the SIMD proposal was cut down to make the Swift 5 schedule so I'm thrilled you're coming back around to improve some of the stuff that was cut just for schedule reasons.

- The rationale for any/all being free functions is inconsistent. I agree with your point that "These two are defined as free functions, because at use sites they read significantly more clearly", but that seems like it applies just as well to the other reductions. Why not make sum/min/max be global functions as well (at least as far as users see them, I understand there will be backing protocol requirements and members for the impl) for consistency?

- The `indexOf*Value` proposals are a bit weird and we don't have a lot of precedent for such operations AFAIK. If you have no obviously great name for them, it might make sense to split them out to a separate discussion, and consider the intersection between their usecases and similar usecases on other collection'y things.

- Similarly to the reduction operations, I don't see why Float4.max(a, b) is better than max(a,b). Is that required? Actually, is the actual issue that these are elementwise operations? If so, it seems really weird to me that Float4.max is elementwise without calling that out. We have the `.` operators to specify elementwise/pointwise operations, but it seems like a prefix word of some sort should be used on named methods to make this explicit.

- If you add a `one` member to SIMD (something I'm generally +1 on) it is worth considering whether we should add them to the scalar types for consistency.

Thanks!

-Chris

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [March 17, 2019, 5:59am UTC](https://forums.swift.org/t/simd-additions/21489/4 "2019-03-17T05:59:18Z")

</div>

> Extending vectors

> Add the following initializers:

```swift
extension SIMD3 {
  /// The vector (xy.x, xy.y, z)
  public init(_ xy: SIMD2<Scalar>, _ z: Scalar)
}

extension SIMD4 {
  /// The vector (xyz.x, xyz.y, xyz.z, w)
  public init(_ xyz: SIMD3<Scalar>, _ w: Scalar)
}

```

I’m worried that these SIMD types are starting to reach into `Vector${N}` territory, as it does sound like a lot of your apple-internal users are using them in-place of proper vector wrappers. I have these same functions in my `vectors.swift.gyb` file (except called `extend` and `homogenize` static functions), but it seems like you’re setting us up for a lot of API overlap once we add true vector types to the language.

> Horizontal operations

Where is horizontal `volume`? I don’t find horizontal add much use except for implementing dot and cross product on the higher level vector type. Horizontal multiply on the other hand is a lot more useful for stuff like computing the number of pixels in an image `size` vector.

> Min, max, clamp

YES. (I would also very much like to see `clamp` on scalar floats, but that already got rejected.) though my same concerns about API overlap apply here too.

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [March 17, 2019, 5:15pm UTC](https://forums.swift.org/t/simd-additions/21489/5 "2019-03-17T17:15:46Z")

</div>

> [@taylorswift](#):
>
> I’m worried that these SIMD types are starting to reach into `Vector${N}` territory, as it does sound like a lot of your apple-internal users are using them in-place of proper vector wrappers. I have these same functions in my `vectors.swift.gyb` file (except called `extend` and `homogenize` static functions), but it seems like you’re setting us up for a lot of API overlap once we add true vector types to the language.

We've been pushing quite hard to keep the two things separate, actually. The particular extending initializers here are closer to the boundary, I agree, but still worth having on the SIMD side of things.

> [@taylorswift](#):
>
> Where is horizontal `volume` ? I don’t find horizontal add much use except for implementing dot and cross product on the higher level vector type. Horizontal multiply on the other hand is a lot more useful for stuff like computing the number of pixels in an image `size` vector.

Literally none of our internal clients have asked for horizontal multiply, I've never wanted it in the course of writing ~10M lines of simd code in other languages, and there's no support for efficient implementation in hardware (so there's no advantage to having the abstraction instead of just using `indices.reduce(into: 1)`.)

As a side note, representing an image `size` as as SIMD vector seems at first glance like an extremely odd use case--what is the SIMD representation buying you over a tuple or array? Does it make sense to perform an elementwise sum of image sizes? Are height and width even the same thing? This seems like a recipe for subtle errors.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [March 17, 2019, 9:43pm UTC](https://forums.swift.org/t/simd-additions/21489/6 "2019-03-17T21:43:38Z")

</div>

> [@scanon](#):
>
> As a side note, representing an image `size` as as SIMD vector seems at first glance like an extremely odd use case--what is the SIMD representation buying you over a tuple or array? Does it make sense to perform an elementwise sum of image sizes? Are height and width even the same thing? This seems like a recipe for subtle errors.

It’s useful for computing offsets/flattened positions, mainly through vector addition and subtraction. In the case of text shaping, having SIMD bitwise right shift is also useful with text shaping APIs, as a lot of them will give 2D coordinates and glyph image dimensions in 1/64s.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [March 18, 2019, 6:51pm UTC](https://forums.swift.org/t/simd-additions/21489/7 "2019-03-18T18:51:51Z")

</div>

I'm confused about the collection initializer. [Don't we have that already](https://developer.apple.com/documentation/swift/simd3/3139913-init) using Slices? Or is it really important to convert the prefix of a Collection to a SIMD vector? That seems bizarre to me.

`indexOfMinValue` and `indexOfMaxValue` don't seem like they belong on SIMD vectors; they're general Collection operations. Do they have significantly faster SIMD implementations?

You _could_ handle `sum` by partitioning on negative/positive and then adding pairs first, but I admit that seems like a lot of work and something that won't compile down to a single instruction.

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [March 18, 2019, 7:17pm UTC](https://forums.swift.org/t/simd-additions/21489/8 "2019-03-18T19:17:59Z")

</div>

> [@jrose](#):
>
> Or is it really important to convert the prefix of a Collection to a SIMD vector? That seems bizarre to me.

The primary use case for these API is "I have an `Array<Float>` that I got from some API, and I want to iterate over groups of three elements, process them as `SIMD3<Float>` and then do something else with them. Yes, this can be done in two steps via slices. That may be an adequate solution, but at least some of our users would like something more direct.

> [@jrose](#):
>
> `indexOfMinValue` and `indexOfMaxValue` don't seem like they belong on SIMD vectors; they're general Collection operations. Do they have significantly faster SIMD implementations?

SIMD doesn't conform to Collection, so even if they're defined on Collection we'd still need some solution for SIMD vectors.

> [@jrose](#):
>
> You _could_ handle `sum` by partitioning on negative/positive and then adding pairs first, but I admit that seems like a lot of work and something that won't compile down to a single instruction.

It actually might on some targets, but yeah, users shouldn't need to do that.

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [March 18, 2019, 9:39pm UTC](https://forums.swift.org/t/simd-additions/21489/9 "2019-03-18T21:39:57Z")

</div>

> [@Chris\_Lattner3](#):
>
> "These two are defined as free functions, because at use sites they read significantly more clearly", but that seems like it applies just as well to the other reductions. Why not make sum/min/max be global functions as well

The `min` and `max` reductions must be methods on SIMD types to be consistent with the same pattern on `Sequence`. They are for selecting a single scalar (element) from the SIMD (sequence). It's clear that the SIMD being reduced should be `self` in these cases. This leaves the `min` free function for when you're comparing two peers, neither of which should be `self`. I think the same logic would apply to `seq.sum` vs `sum(x, y)` if we had it.

`any` and `all` are an unusual case because they aren't operating on a SIMD like `min`/`max` are. They're operating on the result of a SIMD comparison. It seems wrong to make that mask "`self`" in the expression. The member equivalent would need to be something like `if (x .< 0).anyIsTrue( )`, which is ungainly to type, difficult to read, and has discoverability challenges.

> [@Chris\_Lattner3](#):
>
> is the actual issue that these are elementwise operations? If so, it seems really weird to me that Float4.max is elementwise without calling that out.

I think I agree. My other worry is there's a reasonable case to be made that SIMD should be conditionally `Comparable` (lexicographically – this is often requested of `Array`) and if that happens (or if a user does it themselves retroactively) we'll get ambiguity with the other `max`.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [March 18, 2019, 10:13pm UTC](https://forums.swift.org/t/simd-additions/21489/10 "2019-03-18T22:13:07Z")

</div>

> [@scanon](#):
>
> The primary use case for these API is "I have an `Array<Float>` that I got from some API, and I want to iterate over groups of three elements, process them as `SIMD3<Float>` and then do something else with them. Yes, this can be done in two steps via slices. That may be an adequate solution, but at least some of our users would like something more direct.

Let's see…

```swift
for i in stride(from: fullArray.startIndex, to: fullArray.endIndex, by: 3) {
  let next = SIMD3(fullArray[i..<(i+3)])
  doSomethingWith(next)
}

```

Or with offset subscripts:

```swift
var remaining = fullArray[...]
while !remaining.empty {
  let next = SIMD3(remaining[offset: ..<3])
  doSomethingWith(next)
  remaining = remaining[offset: 3...]
}

```

I'm not sure the `init(_:start:)` variant adds enough value.
