[swift-users] Chaining struct-mutating funcs

Dave Abrahams dabrahams at apple.com
Wed Aug 10 14:01:47 CDT 2016


on Tue Aug 09 2016, Fritz Anderson <swift-users-AT-swift.org> wrote:

> I’m sending directly to those who took time over my question, because, per Michael’s request, I have a
> minimal case to attach. Phrases in boldface are for skimmability, not shouting. 
>
> Strip out my use case (I’m encouraged that Dave recapitulated exactly
> what I was asking about). My remaining question is: How do you safely
> reuse a class reference as the backing store for a struct?  Yes, it’s
> done all the time, but the trick requirement is that I want to chain
> the funcs that do it; chained value returns are immutable; and as far
> as I could tell, you can’t get CoW-safe reuse without mutating
> self. The result is that you are forced to make expensive copies of
> the backing store every time. Joe’s solution looked promising in that
> it purported to pass the CoW buffer (if possible) out of the func
> after doing all the mutation internally.
>
> I couldn’t figure out how the answer Joe gave could work: His code
> duplicates the struct’s original reference into a copy of the struct,
> after which he expects the runtime to report (when possible) that no
> such duplicate exists. I asked how this could be, as my attempt to
> replicate in a playground showed the reference was never found unique
> — which is what I had intuitively expected.

Well, a playground is a terrible way to check this, since lifetimes
don't necessarily obey the normal rules, but... you're right, it doesn't
seem to be workable today.

>
>
> He says this fundamental design pattern in Swift works only if you
> change the semantics of the language by turning on the
> optimizer. 

When you count performance characteristics as semantics, yes.

> (Sorry to be all Asperger's about it, but nobody corrected me the
> first time I put it this way.)

Actually I can't even get it to happen with the optimizer on in my
tests.

> I have many, many questions, I might even hazard objections, but
> they’re moot: Optimized or not, that code never reuses the backing
> object.
>
> The attached project was built with Xcode 8.0b5. It uses Joe’s code
> (except I still must use isUniquelyReferencedNonObjC(_:)). I run
> addInts(x:, y:) and check two ways whether the reference was found
> unique. Same result, optimized or not.
>
> It occurred to me that the globals s_x and s_y might bump the reference count. I removed them and used this
> instead:
>
> let s_result = addInts(x: S(c: C(value: 99)), y: S(c: C(value: -98)))
>
> Still no unique references.
>
> I recognize I am taking up a lot of god time mere days before a major
> release, when the likeliest outcome is that I’m a jackass. 

I don't think so.  What's more likely is that we could be optimizing
better. 

> My concern runs deeper than what’s in this message, but I shouldn't
> muddy the waters.  Those deeper things can go back to the public once
> I understand the issue better (or you boot me back).
>
> ---
>
> Context (supplementary, no need to spend time)
>
> If seeing what I’m trying to do helps, I’ve attached my attempt. I’m
> sure there are defects in API style, safety, and correctness. I’d have
> done more if I hadn’t suspended the project over this issue. Class
> FloatBuffer is the backing store; ManagedFloatBuffer is the wrapper
> class that does all the operations. None of the operations are
> declared mutating.
>
> Anything that calls (unary|binary)Operator returns a fresh
> ManagedFloatBuffer every time; structs and their buffers are created
> and initialized every time — intentionally. It seems to work well
> under gentle use.
>
> Callers of mutabilityWrapper preserve the receiver’s backing buffer
> whenever possible and mutate it in-place.  Those funcs always return
> self. These might be a big win; I can’t tell because I’ve never been
> able to get unique references. Because there’s an allocation (possibly
> initialization) every time, they are no better than the
> (unary|binary)Operator funcs. malloc and friends take up a significant
> amount of time on the scale of vector math.
>
> Both flavors can cascade; the only problem is that the “in-place”
> methods don’t live up to the name.

The question of how to optimize the evaluation of non-mutating
expressions using in-place operations is an old one.  Once upon a time
we were pursuing language features for that purpose
(https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst)
but that particular approach is... an ex-approach ;-).

-- 
-Dave



More information about the swift-users mailing list