Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-10605] Enum cases should provide a modify accessor for their contained objects to avoid CoWs #53005

Open
Lukasa opened this issue May 2, 2019 · 7 comments
Labels
compiler The Swift compiler in itself improvement performance

Comments

@Lukasa
Copy link
Contributor

Lukasa commented May 2, 2019

Previous ID SR-10605
Radar rdar://50463362
Original Reporter @Lukasa
Type Improvement
Additional Detail from JIRA
Votes 3
Component/s Compiler
Labels Improvement, Performance
Assignee None
Priority Medium

md5: 23bcbf1465330d1790dbf66f286b21dd

Issue Description:

The following simply code triggers 200k unnecessary CoW operations:

struct Test {
    var backingArray = [UInt8]()
}

enum Elements {
    case first(Test)
    case second
}

struct StateMachine {
    var base: Elements = .first(Test())
}

extension StateMachine {
    @inline(never)
    mutating func appendSomething() {
        switch self.base {
        case .first(var test):
            test.backingArray.append(UInt8.random(in: 0..<100))
            self.base = .first(test)
        case .second:
            break
        }
    }
}

var machine = StateMachine()

for _ in 0..<200_000 {
    machine.appendSomething()
}

Arguably these CoW operations are necessary in terms of Swift's current accessor semantics, which is why I've filed this as an enhancement request and not a bug, but they make it very difficult to implement performant state machines using Swift enumerations and associated data.

@belkadan
Copy link
Contributor

belkadan commented May 3, 2019

There are sort of two ways to go about this:

  • Actually add a language feature (probably case .first(inout test))

  • Just add the modify accessor for a case and allow it to be used as an optimization when the value is soon to be dead.

I think it'd be valid to do the latter without breaking ABI as long as it's not in libraries with -enable-library-evolution just yet.

cc @bob-wilson, @atrick, @rjmccall

@rjmccall
Copy link
Member

rjmccall commented May 3, 2019

I don't know what a `modify` accessor has to do with this; `case` is always totally physical. Point 2 is just saying that we could recognize that `self.base` is dead on that path and can be destroyed immediately, making `test` hold a unique reference. I agree that that's a possible optimization here.

@belkadan
Copy link
Contributor

belkadan commented May 3, 2019

Oh, right, that would work too.

@atrick
Copy link
Member

atrick commented May 4, 2019

`self` is the address of a mutable object. `test` is a value loaded from that object. The basic challenge is to analyze all paths from the load of `self.base` to the point at which it is rewritten. If there are no other reads from `self.base`, then the load of Elements can be converted to a load_borrow.

The bad news is that's much easier said than done (it's hard to see how a series of algebraic SIL transforms will eventually lead to the optimize. It looks like one heroic analysis is needed to me). One sticking point is that the copy occurs when Elements is loaded, before switching over the cases. So removing the copy is not at all straightforward.

The good news is that this is the sort of thing will be much easier to do now that we have exclusivity. It will also probably be easier with SIL ownership-SSA. I feel compelled to point out that SIL opaque values will not help here.

It's something we should prioritize, but might not happen until after some fundamental SIL rearchitecting is done:

<rdar://problem/50463362> [SR-10605] Enum cases should provide a modify accessor for their contained objects to avoid CoWs

Great test case!

@eeckstein
Copy link
Member

@Lukasa Adding a self.base = .second before modifying the payload should workaround the problem.

@Lukasa
Copy link
Contributor Author

Lukasa commented May 6, 2019

Thanks Erik, we’ve got a couple of workarounds in place for this already that rely on that.

@atrick
Copy link
Member

atrick commented May 6, 2019

Correction to my comment above: SIL ownership is available late enough now that we might be able to inject this optimization early enough in the pipeline without being blocked on other work.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler The Swift compiler in itself improvement performance
Projects
None yet
Development

No branches or pull requests

5 participants