Is there a runtime cost for trivial computed properties?

I'm working on a performance critical section, and it would be very useful to implement a wrapper pattern like so:

protocol MyProtocol {
    var someValue: Int { get }
}

struct Wrapped: MyProtocol {
    var someValue: Int
}

struct Wrapper: MyProtocol {
    var wrapped: Wrapped
    var someValue: Int {
        return wrapped.someValue
    }
}

So I can imagine that the compiler could optimize the access on Wrapper.someValue so that it would be equivalent to Wrapped.someValue since in either case this is essentially just a memory access at a fixed offset in the struct, but is this actually the case in practice, or is there additional overhead from this type of wrapping?

This depends on a few things. Your example (not publics) seem to suggest that this all happens within one module. If that is the case, then you can get the Swift compiler to emit just a memory load. The only thing you need to do is use it as a generic (and not as an existential). So for example in this example:

protocol MyProtocol {
    var someValue: Int { get }
}

struct Wrapped: MyProtocol {
    var someValue: Int
}

struct Wrapper: MyProtocol {
    var wrapped: Wrapped
    var someValue: Int {
        return wrapped.someValue
    }
}

@inline(never) // Just to actually get this method emitted
func testStuff<W: MyProtocol>(_ w: W) -> Int {
	return w.someValue &+ 0xdead
}

the disassembly (needs to be compiled with optimisations) is:

generic specialization <test.Wrapper> of test.testStuff<A where A: test.MyProtocol>(A) -> Swift.Int:
0000000100000d50        pushq   %rbp
0000000100000d51        movq    %rsp, %rbp
0000000100000d54        leaq    0xdead(%rdi), %rax // this is the load & the add
0000000100000d5b        popq    %rbp
0000000100000d5c        retq

If you want to achieve the same across modules, so for example if it were public func testStuff<W: MyProtocol>(...), then you would either need to make it @inlinable public func testStuff<...>(...) and mark a few more things as @inlinable/@usableFromInline or compile with Swift 5.2's new -cross-module-optimisation switch :slight_smile:.

3 Likes