[swift-dev] [idle] COW wrapper in 30 lines

Arnold Schwaighofer aschwaighofer at apple.com
Thu Mar 10 09:43:36 CST 2016


Nice!

As you have mentioned, unfortunately, this does not work on nested types without spurious copies (e.g. for foo.left.value.right.value = …). You would need to implement this with a mutableAddressor. Unfortunately, doing so requires Builtins and so this can’t currently be done outside of the standard library (without nasty flags).

-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-A-copy-on-write-data-type.patch
Type: application/octet-stream
Size: 4157 bytes
Desc: not available
URL: <https://lists.swift.org/pipermail/swift-dev/attachments/20160310/70202fbf/attachment.obj>
-------------- next part --------------


I agree with you that some interface forwarding would be an awesome language feature to have.

It would allow for implementing an indirect storage wrapper without lots of boilerplate.

Consider an example where you have value types that implement a protocol:


public _MyProtoWithoutAsscociatedType {
}

public protocol MyProtocol : MyProtoWithoutAsscociatedType {
  associatedtype Assoc

  public func doThis(v : Assoc)
  public func doThat()

  public static func initAssocType() -> Assoc

}

struct MyProtocolImplementation : MyProtocol {
  typealias Assoc = Int
  var a : AReference
  var b : AReference
  var c : AReference
  var d : Double

  func doThis(v: Assoc) {…}
  func doThat() {…}

  static func initAssocType() -> Assoc {
    return 0
  }
}

Your application passes those value types as existential values or as generic values because the architecture requires that. This can incur lots of allocations at every value copy for this example value type because we don’t fit in the inline buffer (three pointer words) which would be allocated inline (on the stack or as a stored property for existentials).

One way to work around this is today is make your value type’s storage indirect via a wrapper such that it will always fit in inline storage.

class _BoxStorage<T> {
    final var value: T

    init(_ elt: T) {
        value = elt
    }
}

public struct Indirect<V : MyProtocol> : MyProcotol {

    public typealias Assoc = V.Assoc

    final var _storage: _BoxStorage<V>

    public init(_ elt: V) {
        _storage = _BoxStorage(elt)
    }

    internal var value: V {
        get {
            return _storage.value
        }
        mutableAddressWithNativeOwner {
            if !isUniquelyReferencedNonObjC(&_storage) {
                _storage = _BoxStorage(_storage.value)
            }
            return (
                UnsafeMutablePointer(Builtin.addressof(&_storage.value)),
                Builtin.castToNativeObject(_storage))
        }
    }

    public static func initAssocType() -> Assoc {
        return Assoc.initAssocType()
    }

    public func doThis(v: Assoc) {
      value.doThis(v)
    }
    public func doThat() {
      value.doThat()
    }
}

Indeed, doing so would require a lot of boiler plate.


Instead the following would be much nicer:

public struct Indirect<V : MyProtocol> : MyProcotol {

    public typealias Assoc = V.Assoc

    final var _storage: _BoxStorage<V>

    public init(_ elt: V) {
        _storage = _BoxStorage(elt)
    }

    internal forwarding var value: V as MyProtocol {
        typealias Assoc = V.Assoc

        get {
            return _storage.value
        }
        mutableAddressWithNativeOwner {
            if !isUniquelyReferencedNonObjC(&_storage) {
                _storage = _BoxStorage(_storage.value)
            }
            return (
                UnsafeMutablePointer(Builtin.addressof(&_storage.value)),
                Builtin.castToNativeObject(_storage))
        }
    }

}



> On Mar 9, 2016, at 9:53 AM, Jordan Rose via swift-dev <swift-dev at swift.org> wrote:
> 
> Ha, I remember this document, but forgot it included an implementation. Encouraging to see that it's pretty much identical.
> 
> The concrete type forwarding logic is something we ought to be able to simplify somehow (with new language features).
> 
> Jordan
> 
> 
>> On Mar 9, 2016, at 9:50 , Nadav Rotem <nrotem at apple.com> wrote:
>> 
>> HI Jordan, 
>> 
>> Very Nice!    
>> 
>> There is a similar implementation and discussion about performance here:
>> 
>> https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst#advice-use-copy-on-write-semantics-for-large-values
>> 
>> -Nadav
>> 
>> 
>>> On Mar 9, 2016, at 9:39 AM, Jordan Rose via swift-dev <swift-dev at swift.org> wrote:
>>> 
>>> Just for fun, I wrote a wrapper for COW types that don't need the flexible inline storage of ManagedBuffer. It turned out to be pretty straightforward, though I didn't bother with materializeForSet and thus am incurring the cost of many extra value copies on mutation. The major downside is having to forward all operations through, something I'm sure the implementers of Array and Dictionary are very used to!
>>> 
>>> Disclaimer: This is not performant; don't use it in your app.
>>> 
>>> Jordan
>>> 
>>> <cow.swift>
>>> _______________________________________________
>>> swift-dev mailing list
>>> swift-dev at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-dev
>> 
> 
> _______________________________________________
> swift-dev mailing list
> swift-dev at swift.org
> https://lists.swift.org/mailman/listinfo/swift-dev



More information about the swift-dev mailing list