Since your backing buffer is copy-on-write, you can do the in-place mutation 
optimization in your immutable implementations, something like this:

class C {
  var value: Int
  init(value: Int) { self.value = value }
}

struct S { var c: C }

func addInts(x: S, y: S) -> S {
  var tmp = x
  // Don't use x after this point so that it gets forwarded into tmp
  if isKnownUniquelyReferenced(&tmp.c) {
    tmp.c.value += y.c.value
    return tmp
  } else {
    return S(c: C(value: tmp.c.value + y.c.value))
  }
}

which should let you get similar efficiency to the mutating formulation while 
using semantically immutable values.

-Joe

> On Aug 5, 2016, at 2:35 PM, Fritz Anderson via swift-users 
> <[email protected]> wrote:
> 
> Swift 3 as of Xcode 8.0b4
> 
> TL;DR: I have a struct value type backed by a copy-on-write mutable buffer. 
> You use it to perform arithmetic on the buffers. The most expressive way to 
> do this efficiently is to chain the arithmetic operators so each mutates the 
> same buffer. Swift does not like to chain mutating operators — it treats the 
> result of each step as immutable, so you can’t continue the chain. I can’t 
> argue; the syntax apparently can't express anything else.
> 
> All the alternatives I see are ugly-to-dangerous.
> 
> Have I missed something, I hope? Please make a fool of me.
> 
>       — F
> 
> The details of my use case or implementation are off-topic; even if mine are 
> ill-considered, surely apt ones exist. Unless you can demonstrate there are 
> none.
> 
> The vDSP_* functions in Apple’s Accelerate framework are declared in C to 
> operate on naked float or double pointers. I decided to represent such Float 
> buffers in Swift by a struct (call it ManagedFloatBuffer) containing a 
> reference to a FloatBuffer, which is a final specialization of class 
> ManagedBuffer<Int, Float>.
> 
> (The names are a work-in-progress. Just remember: ManagedFloatBuffer is a 
> value type that can copy-on-write to a reference to FloatBuffer, a backing 
> store for a bunch of Floats.)
> 
> The nonmutating funcs:
> 
>     func subtract(_ other: ManagedFloatBuffer) -> ManagedFloatBuffer
>     func subtract(_ scalar: Float) -> ManagedFloatBuffer
> 
> are straightforward. They return new ManagedFloatBuffer values. You can chain 
> further calls to simplify a complex calculation that is neither intricate nor 
> tied up in temporaries:
> 
>     let sum²OfResiduals = speeds
>         .subtract(cameraSpeed.mean)
>         .multiply(feetToMeters)
>         .sumOfSquares
> 
> Great. And vDSP gets you about a 40% boost. (The compiler itself seems to do 
> a pretty good job of auto-vectorizing; the unoptimized code is a couple of 
> orders of magnitude slower.) But as you chain the immutables, you generate 
> new FloatBuffers to hold the intermediate results. For long chains, you end 
> up allocating new buffers (which turns out to be expensive on the time scale 
> of vectorized math) and copying large buffers into them that you are about to 
> discard. I want my Swift code to be as performant as C, but safer and more 
> expressive.
> 
> So how about some mutating functions to change a ManagedFloatBuffer’s bytes 
> in-place (copying-on-write as needed so you can preserve intermediate values)?
> 
>     mutating func reduce(by other: ManagedFloatBuffer) -> ManagedFloatBuffer
>     mutating func reduce(by scalar: Float) -> ManagedFloatBuffer
> 
> These return self, because I’d hoped I could chain operators as I did with 
> the non-mutating versions.
> 
> The compiler doesn’t like this. It says reduce(by:) returns an immutable 
> value, so you can’t chain mutating functions.
> 
> (I can see an issue in that when the first func's self is copied as the 
> return value that is used as the second func’s self,  that could make two 
> surviving references to the same buffer, so a buffer copy would happen when 
> you mutate the second func’s self anyway. I’m not sure the compiler has to do 
> that, but I can see how it might be hard to account for otherwise. Hey, it’s 
> a tail call, right? SMOP, not source-breaking at all.)
> 
> StackOverflow invites me to eat cake: Make the mutable operand inout to funcs 
> I call one by one. Something like:
> 
>     multiply(perspectiveCorrections, into: &pixelXes)
>     sin(of: &pixelXes)
>     multiply(pixelXes, into: &speeds)
>     multiply(feetToMeters, into: &speeds)
>     subtract(cameraSpeed.mean, from: &speeds)
>     let sumSquaredOfResiduals = speeds.sumOfSquares
> 
>     // grodiness deliberately enhanced for illustration
> 
> I’d rather not. The thing to be calculated is named at the bottom of the 
> paragraph. The intermediate steps must preserve names that change meaning 
> line-by-line. You have to study the code to recognize it as a single 
> arithmetic expression.
> 
> And by the by, if a vector operand is itself the result of a mutating 
> operation, the dependency graph becomes a nightmare to read — I can’t be sure 
> the illustration even expresses a plausible calculation.
> 
> Thinking up more reasons to hate this solution is a fun parlor game you and 
> your family can play at home.
> 
> Strictly speaking, the compiler is right: I don’t see any language construct 
> that expresses that a returned value type that may be mutated by a chained 
> func. Am I correct?
> 
> I’m not at all happy with turning ManagedFloatBuffer into a class. 
> Intuitively, this is a value type. Passing a packet of Floats into a func (or 
> into another thread, as one does with math) and finding your Floats had 
> changed in the mean time is… surprising.
> 
> I’m not optimistic, but I have to ask: Is there a way to do this — to take 
> mutability down an operator chain?
> 
>       — F
> 
> 
> 
> 
> 
> 
> 
> _______________________________________________
> swift-users mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to