Guaranteed vs Owned
// # Why +0 is a better default than +1 for "normal function arguments". | |
// | |
// My intention here is to show why +0 is better default in a resilient | |
// word. Keep in mind that inside individual modules and with inlinable | |
// declarations, the optimizer can change conventions at will so from a defaults | |
// perspective, these are not interesting. The interesting case is calling | |
// non-inlinable functions in other modules. | |
// | |
// Consider a situation where I have a class Klass and a function foo that calls | |
// a function bar in a different module. | |
class Klass {} | |
func foo(_ k: Klass) { | |
bar(k) | |
bar(k) | |
bar(k) | |
} | |
// Bar is in a different module and it is not inlinable so we can't inline or | |
// analyze its body. It is completely opaque beyond its declaration. | |
func bar(_ k: Klass) { | |
// ... | |
} | |
//------------------------------------------------------------------------------ | |
// Using "pseudo-swift" this lowers to the following retain traffic at +1. | |
class Klass {} | |
// k comes in at +1. | |
func foo(_ k: Klass) { | |
// retain(k) | |
bar(k) // release inside bar | |
// retain(k) | |
bar(k) // release inside bar | |
// retain(k) | |
bar(k) // release inisde bar. | |
// release(k) | |
} | |
// Again bar is in a different module, so we can't see its body. Even so, | |
// convention wise we know that there must be a release at the end. | |
func bar(_ k: Klass) { | |
// ... | |
// release(k) | |
} | |
// Since we can not analyze or inline bar, we can not eliminate the releases | |
// in bar. We /could/ coalesce the retains into one large add to the refcount, | |
// but the releases are not able to be optimized, resulting in slower code. | |
//------------------------------------------------------------------------------ | |
// Now let us consider the +0 world. In that case, we have the following | |
// pseudo-swift. | |
class Klass {} | |
// k comes in at +0. | |
func foo(_ k: Klass) { | |
// retain(k) | |
bar(k) | |
// release(k) | |
// retain(k) | |
bar(k) | |
// release(k) | |
// retain(k) | |
bar(k) | |
// release(k) | |
} | |
func bar(_ k: Klass) { | |
// ... | |
} | |
// Notice how since the retains/releases are all in the caller, the optimizer | |
// can eliminate /all/ the retain/release traffic and we can actually achieve optimal | |
// code without ref counts. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment