Inout vs pass by reference swift optimization - swift

I have a very large list of numbers and I would like to pass it to a function to do some manipulations to it. Originally, I created a function with an inout property. As many know, inout in swift does NOT pass by reference, rather it makes an initial copy to the function then copies the values back on the return. This sounds expensive. I decided to wrap my list in a class and pass by reference in order to optimize and reduce time for copying. Interestingly enough, it seems that the inout function is faster than the pass by reference function. I even make a manipulation to the inout variable to cause the compiler to copy-on-write. Any ideas why the inout function is faster than the pass by reference?
class ReferencedContainer {
var container = [Int:Bool]()
}
func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
func inoutTest(list: inout [Int]?) -> [Int]? {
list![0]=1
return list
}
func refTest(list: ReferencedContainer) -> ReferencedContainer {
list.container[0] = true
return list
}
var list : [Int]? = [Int]()
for i in 0...10000 {
list?.append(i)
}
var ref = ReferencedContainer()
for i in list!
{
ref.container[i] = true
}
printTimeElapsedWhenRunningCode(title: "refTest", operation: { refTest(list: ref)})
printTimeElapsedWhenRunningCode(title: "inout", operation: { inoutTest(list: &list)})
Time elapsed for refTest: 0.0015590190887451172 s.
Time elapsed for inout: 0.00035893917083740234 s.

As many know, inout in swift does NOT pass by reference, rather it makes an initial copy to the function then copies the values back on the return. This sounds expensive.
It's not. It's copying an instance of a [Int: Bool] (a.k.a. Dictionary<Int, Bool>) which is only a single pointer. See for yourself, with print(MemoryLayout<[Int: Bool]>.size).

Two things to consider, out of many possible:
inout may use call-by-reference as an optimisation, see In-Out Parameters
Your two tests are rather different, one uses optionals and an array, the other a dictionary. You're also only testing a single mutation, try doing more. The only difference you should have is struct vs. class and if you try that you can get similar speeds or one of them faster than the other – either way around.

Related

Is `inout` the correct way to prevent large array copies when passing parameters? [duplicate]

When passing a class or primitive type into a function, any change made in the function to the parameter will be reflected outside of the class. This is basically the same thing an inout parameter is supposed to do.
What is a good use case for an inout parameter?
inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed-in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.
For example:
import UIKit
var num1: Int = 1
var char1: Character = "a"
func changeNumber(var num: Int) {
num = 2
print(num) // 2
print(num1) // 1
}
changeNumber(num1)
func changeChar(inout char: Character) {
char = "b"
print(char) // b
print(char1) // b
}
changeChar(&char1)
A good use case will be swap function that it will modify the passed-in parameters.
Swift 3+ Note: Starting in Swift 3, the inout keyword must come after the colon and before the type. For example, Swift 3+ now requires func changeChar(char: inout Character).
From Apple Language Reference: Declarations - In-Out Parameters:
As an optimization, when the argument is a value stored at a physical
address in memory, the same memory location is used both inside and
outside the function body. The optimized behavior is known as call by
reference; it satisfies all of the requirements of the copy-in
copy-out model while removing the overhead of copying. Do not depend
on the behavioral differences between copy-in copy-out and call by
reference.
If you have a function that takes a somewhat memory-wise large value type as argument (say, a large structure type) and that returns the same type, and finally where the function return is always used just to replace the caller argument, then inout is to prefer as associated function parameter.
Consider the example below, where comments describe why we would want to use inout over a regular type-in-return-type function here:
struct MyStruct {
private var myInt: Int = 1
// ... lots and lots of stored properties
mutating func increaseMyInt() {
myInt += 1
}
}
/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
function property is mutated
function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation
Also, in the example above---disregarding memory issues---inout can be preferred simply as a good code practice of telling whomever read our code that we are mutating the function caller argument (implicitly shown by the ampersand & preceding the argument in the function call). The following summarises this quite neatly:
If you want a function to modify a parameter’s value, and you want
those changes to persist after the function call has ended, define
that parameter as an in-out parameter instead.
From Apple Language Guide: Functions - In-Out Parameters.
For details regarding inout and how it's actually handled in memory (name copy-in-copy-out is somewhat misleading...)---in additional to the links to the language guide above---see the following SO thread:
Using inout keyword: is the parameter passed-by-reference or by copy-in copy-out (/call by value result)
(Edit addition: An additional note)
The example given in the accepted answer by Lucas Huang above tries to---in the scope of the function using an inout argument---access the variables that were passed as the inout arguments. This is not recommended, and is explicitly warned not to do in the language ref:
Do not access the value that was passed as an in-out argument, even if
the original argument is available in the current scope. When the
function returns, your changes to the original are overwritten with
the value of the copy. Do not depend on the implementation of the
call-by-reference optimization to try to keep the changes from being
overwritten.
Now, the access in this case is "only" non-mutable, e.g. print(...), but all access like this should, by convention, be avoided.
At request from a commenter, I'll add an example to shine light upon why we shouldn't really do anything with "the value that was passed as an in-out argument".
struct MyStruct {
var myStructsIntProperty: Int = 1
mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
myStructsIntProperty += 1
/* What happens here? 'myInt' inout parameter is passed to this
function by argument 'myStructsIntProperty' from _this_ instance
of the MyStruct structure. Hence, we're trying to increase the
value of the inout argument. Since the swift docs describe inout
as a "call by reference" type as well as a "copy-in-copy-out"
method, this behaviour is somewhat undefined (at least avoidable).
After the function has been called: will the value of
myStructsIntProperty have been increased by 1 or 2? (answer: 1) */
myInt += 1
}
func myInoutFunction (inout myInt: Int) {
myInt += 1
}
}
var a = MyStruct()
print(a.myStructsIntProperty) // 1
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 2
a.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.
So, in this case, the inout behaves as copy-in-copy-out (and not by reference). We summarize by repeating the following statement from the language ref docs:
Do not depend on the behavioral differences between copy-in copy-out
and call by reference.
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
inout param: modifies passing and local variable values.
func doubleInPlace(number: inout Int) {
number *= 2
print(number)
}
var myNum = 10 doubleInPlace(number: &myNum)
inout parameter allow us to change the data of a value type parameter and to keep changes still after the function call has finished.
If you work with classes then, as you say, you can modify the class because the parameter is a reference to the class. But this won't work when your parameter is a value type (https://docs.swift.org/swift-book/LanguageGuide/Functions.html - In-Out Parameters Section)
One good example of using inout is this one (defining math for CGPoints):
func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
func += (left: inout CGPoint, right: CGPoint) {
left = left + right
}
Basically it is useful when you want to play with addresses of variable its very useful in data structure algorithms
when use inout parameter swift 4.0 Work
class ViewController: UIViewController {
var total:Int = 100
override func viewDidLoad() {
super.viewDidLoad()
self.paramTotal(total1: &total)
}
func paramTotal(total1 :inout Int) {
total1 = 111
print("Total1 ==> \(total1)")
print("Total ==> \(total)")
}
}

A function with parameters that doesn't require its parameters at the call site?

I am wondering why I am not required to provide any parameters at the call site of the function on line 19
I have a strong feeling its because of the static keyword on the function but I still would like more clarity / insight. Thanks
Code in text form below (if it helps)
import SwiftUI
import Combine
final class CurrentTime: ObservableObject {
#Published var seconds: TimeInterval = CurrentTime.currentSecond(date: Date())
private let timer = Timer.publish(every: 0.2, on: .main, in: .default).autoconnect()
private var store = Set<AnyCancellable>()
init() {
timer.map(Self.currentSecond).assign(to: \.seconds, on: self).store(in: &store) // <-- Why am I not asked for a parameter here
}
private static func currentSecond(date: Date) -> TimeInterval {
let components = Calendar.current.dateComponents([.year, .month, .day], from: date)
let referenceDate = Calendar.current.date(from: DateComponents(year: components.year!, month: components.month!, day: components.day!))!
return Date().timeIntervalSince(referenceDate)
}
}
TL:DR There is no "call site". You are referring to a function, not calling it.
Let's take a simpler case:
let array = [1,2,3]
func doubler(_ i:Int) -> Int { i*2 }
let array2 = array.map(doubler)
print(array2) // [2,4,6]
The goal is to use map to multiply every element of an array of Int by 2. And we did that.
Now, we could have done it like this:
let array2 = array.map {$0*2}
But what is that {$0*2}? It's shorthand for this:
let array2 = array.map { (i:Int) -> Int in
let i2 = i*2
return i2
}
In other words, the curly braces are the body of an anonymous function that takes an Int and returns an Int.
But no law says that the map parameter must be an anonymous function. It happens we already have a real function that takes an Int and returns an Int — doubler.
So instead of a function body in curly braces, we can pass the named function. How? By a function reference. We are not calling doubler, we are telling map the name of the function that it will call.
The simplest way to form a function reference is just to use the bare name of the function. It isn't the only way, but there's no problem using it here. (For more about function reference syntax, see my How do I resolve "ambiguous use of" compile error with Swift #selector syntax?)
Extra for experts: my favorite use of this shorthand is:
let sum = array.reduce(0, +)
print(sum) // 6
What just happened??? The same thing: in Swift, + is the name of a function.
currentSecond is a function that takes a Date and returns a TimeInterval.
map is declared like this:
func map<T>(_ transform: #escaping (Date) -> T) -> Publishers.Map<Timer.TimerPublisher, T>
Its parameter type is (Date) -> T. T is a generic parameter that can be any type of your choice.
(Date) -> T is the type of functions that takes a Date as parameter and return T.
That's exactly what currentSecond is, if you set T to be TimeInterval. This is why you can pass Self.currentSecond, a function, to map. Also note that you are not calling the function at this point. map decides when to call it, and how many times to call it, if ever.
You can't pass Self.currentSecond(date: someParameter) because that would be passing a TimeInterval to map, and map doesn't want that.
static just means that the function belongs to the class, rather than individual instances of the class. The author probably thought it made more sense this way. Syntactically though, there's nothing stopping you from removing static, in which case you should also remove Self..

How come I can create a variable with same name as a function parameter?

In a function, if I'm being passed an int (which is immutable type, i.e let n), how come if I pass this into a function, I can create a var variable of this? Just wondering why swift works this way, and the reason for it .
func checkNum(_ n: Int) -> Bool {
var n = n
}
In this, it let's me do this.
This is called "shadowing" and is allowed in a variety of places because it's convenient for the programmer because it avoids having two similarly named variables in scope. Without this feature, this code would be:
func checkNum(_ n: Int) -> Bool {
var writableN = n
...
}
This raises the possibility of modifying writableN, and then using n again later in the function unintentionally. It also can make the function a bit harder to understand because of the extra variable. (Of course shadowing can also make code more difficult to understand as well, if it's used without care.)
This is very similar to the if let shadowing, such as:
var x: Int? = ...
if let x = x { ... }
This allows you to use x inside the if let as a non-Optional rather than having to come up with some other name for it.
This is a fairly general feature. Whenever there is a new scope, variables can shadow. For example, you can declare variables inside a function that have the same name as properties or globals. You can create scopes inside a function as well
func checkNum(_ n: Int) -> Bool {
var n = n // Shadows previous `let`
do {
let n = 4 // Shadows previous `var`
print(n)
}
return true
}
Used with care, this is helpful. Used too often (as this last example definitely does), it can make the code very confusing.
Your specific case touches on another important aspect of Swift, which is that it tries to control shared mutable state. The n that the function receives is a copy of the integer that was passed. Modifying n would never directly modify the caller's variable. (There's inout to allow this, but it's subtle. It does't share state. It copies the value in, and on return, copies the value back out.)
Since n isn't shared with the caller, Swift makes this very explicit by making it immutable. If you want to modify it, you need to explicitly make another copy, and var n = n does that.
'var' as a parameter attribute has been deprecated in Swift 3 so that is no longer possible. You can get a similar behaviour by using an inout parameter
func checkNum(_ n: inout Int) -> Bool {
n + 1
}
Remember to call the function by passing the parameter like this: checkNum(&n)

Capturing a struct reference in a closure doesn't allow mutations to occur

I am trying to see if I can use structs for my model and was trying this. When I call vm.testClosure(), it does not change the value of x and I am not sure why.
struct Model
{
var x = 10.0
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
testClosure =
{
() -> () in
model.x = 30.5
}
}
}
var vm = ViewModel(model:&m)
m.x
vm.testClosure()
m.x
An inout argument isn't a reference to a value type – it's simply a shadow copy of that value type, that is written back to the caller's value when the function returns.
What's happening in your code is that your inout variable is escaping the lifetime of the function (by being captured in a closure that is then stored) – meaning that any changes to the inout variable after the function has returned will never be reflected outside that closure.
Due to this common misconception about inout arguments, there has been a Swift Evolution proposal for only allowing inout arguments to be captured by #noescape closures. As of Swift 3, your current code will no longer compile.
If you really need to be passing around references in your code – then you should be using reference types (make your Model a class). Although I suspect that you'll probably be able to refactor your logic to avoid passing around references in the first place (however without seeing your actual code, it's impossible to advise).
(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)
Instances of the closure will get their own, independent copy of the captured value that it, and only it, can alter. The value is captured in the time of executing the closure. Let see your slightly modified code
struct Model
{
var x = 10.0
mutating func modifyX(newValue: Double) {
let this = self
let model = m
x = newValue
// put breakpoint here
//(lldb) po model
//▿ Model
// - x : 30.0
//
//(lldb) po self
//▿ Model
// - x : 301.0
//
//(lldb) po this
//▿ Model
// - x : 30.0
}
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
model.x = 50
testClosure =
{ () -> () in
model.modifyX(301)
}
model.x = 30
}
}
let mx = m.x
vm.testClosure()
let mx2 = m.x
Here is what Apple says about that.
Classes and Structures
A value type is a type that is copied when it is assigned to a
variable or constant, or when it is passed to a function. [...] All
structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behaviour for that method. The method can then mutate (that is,
change) its properties from within the method, and any changes that it
makes are written back to the original structure when the method ends.
The method can also assign a completely new instance to its implicit
self property, and this new instance will replace the existing one
when the method ends.
Taken from here

How to share stream position betwixt functions in Swift?

I was surprised by the following playground I created after seeing some unexpected behavior in my code:
import Foundation
let bytes:[UInt8] = [20, 30, 40, 50, 60, 70]
var stream = bytes.generate()
func consumeTwo(var stream:IndexingGenerator<[UInt8]>) {
print(stream.next())
print(stream.next())
}
consumeTwo(stream) // This prints 20 and 30
print(stream.next()) // This prints 20!? I expected 40!
I had thought that marking the stream argument as var to the consumeTwo() function, the stream state/position would be shared/updated as it moved from function to function. But that appears to not be the case.
Does this mean I need to make that an inout? And pass with the ampersand? When does one use the var if that's the case?
More generally... what is the right/idiomatic way to create a stream over a byte array which can be passed from function to function (e.g. a decoder) and preserve the position of the stream as it is passed around?
+1 for archaic English in the question title. :)
When you use var in a function signature, you create a local copy of that value. It's the same as if you did this:
func consumeTwo(stream: IndexingGenerator<[UInt8]>) {
var localStream = stream
print(localStream.next())
print(localStream.next())
}
When the parameter is a reference type (i.e. a class), the duplicate "value" is a duplicate reference to the same object. But the thing you get from Array.generate() is a value type, so your local copy is a separate iterator with separate state.
Does this mean I need to make that an inout? And pass with the ampersand?
Yes — for your simple example, inout (and pass with &) is a simple and idiomatic way to do this:
func consumeTwo(inout stream:IndexingGenerator<[UInt8]>) {
print(stream.next())
print(stream.next())
}
consumeTwo(&stream) // This prints 20 and 30
print(stream.next()) // This prints 40
Remember: when you want to modify a value type inside a function and later see those modifications outside the function, use inout. And the & goes with it, so that it's clear both inside the function and at the call site that this behavior is happening.
When does one use the var if that's the case?
Use var for parameters only when you want to make a copy that's local to the function invocation. Admittedly, the use cases for this are few. Here's a contrived (and completely unnecessary) one:
func bananify(var strings: [String]) {
for i in 1.stride(to: strings.count, by: 2) {
strings[i] = "banana"
}
print(strings.joinWithSeparator(" "))
}
let words = ["foo", "bar", "bas", "zap", "asdf"]
bananify(words) // "foo banana bas banana asdf\n"
If you find this confusing, you're not the only one. For this reason, removing the ability to use var for parameters is a planned change for Swift 3.
More generally... what is the right/idiomatic way to create a stream over a byte array which can be passed from function to function (e.g. a decoder) and preserve the position of the stream as it is passed around?
As user3441734 notes, you can indeed create and use a reference-type iterator instead. Or you can write a reference type that holds and manages an iterator. For your hypothetical case of sharing a stream among several subsystems of a program, this is probably a good approach — representing a shared resource is one of the canonical cases for using reference types.
you wrote "It makes me wish that Generators were objects instead of structs."
there is no trouble define some generator as reference type ...
class G: AnyGenerator<Int> {
var i = 0
override func next() -> Int? {
return i++
}
}
let g = G()
func foo(gen: G)->Void {
print(gen.next())
print(gen.next())
}
foo(g)
print(g.next())
/*
Optional(0)
Optional(1)
Optional(2)
*/