Skip to content

Instantly share code, notes, and snippets.

@gottesmm
Created June 13, 2018 17:27
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gottesmm/524fca6a4e9fb3d5736a1b9d6686c5e8 to your computer and use it in GitHub Desktop.
Save gottesmm/524fca6a4e9fb3d5736a1b9d6686c5e8 to your computer and use it in GitHub Desktop.
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