Swift tanking performance by excessively copying enums when pattern matching?

I wrote a property wrapper that works around this issue and the compiler is able to optimize into a faster set of instructions, based on a solution discovered by @Karl, assuming (like me) you're stuck working with the current version of Swift that has this issue and not the nightly, which seems to have largely fixed it.

@propertyWrapper
struct ExpensiveValue<Value> {
    
    var wrappedValue: Value
    
    var projectedValue: Value { optimizableFastGet() }
    
    func optimizableFastGet() -> Value {
        //  Figure out the byte offset of Value, and return a copy of it
        
        var copy = self
        
        return withUnsafeMutablePointer(to: &copy) {
            copyPtr in
            
            let rawCopyPtr = UnsafeRawPointer(copyPtr)
            
            //  MemoryLayout.offset(of:) would give us the value's byte offset too.
            //  The downside of that method is that it doesn't work well with generic types,
            //  when the compiler can't directly "see" what property the key path references
            //  and instead dynamically generates a key path at runtime, which is _expensive_.
            //
            //  Honestly Swift's algorithm for the memory layout of structs means that
            //  wrappedValue will always be stored at byte offset 0,
            //  but that could change and therefore can't be relied upon.
            
            let byteOffset = withUnsafeMutablePointer(to: &copyPtr.pointee.wrappedValue) {
                rawCopyPtr.distance(to: $0)
            }
            
            //  Safety checks
            
            assert(
                MemoryLayout<Self>.offset(of: \.wrappedValue) == byteOffset,
                "wrappedValue stored outside of self!"
            )
            
            precondition(
                (0..<MemoryLayout<Self>.size).contains(byteOffset),
                "wrappedValue stored outside of self!"
            )
            
            return rawCopyPtr.load(fromByteOffset: byteOffset, as: Value.self)
        }
    }
    
}
1 Like