Why are swift functions so expensive?
I have been writing an app using SpriteKit. Every frame, I recalculate the position of the CameraNode in the update() function. Lets call this the //(cameraNodeCode). This current setup had little influence on the frames per second, it stayed at 60.
override func update() {
//(cameraNodeCode)
}
As the cameraNodeCode is quite large, I thought it would be better to simplify my code and put it into a function: updateCameraNode(). Now this was what I had:
func updateCameraNode() {
//(cameraNodeCode)
}
override func update() {
updateCameraNode()
}
When I set up the code like this, the frames per second suddenly dropped to 20. I was very surprised as I didn't think functions were this expensive to call. I decided to test my theory with this code:
func emptyFunction() {
}
override func update() {
emptyFunction()
emptyFunction()
emptyFunction()
emptyFunction()
emptyFunction()
}
As I predicted, when I did this the frames per second dropped drastically, to 4.1 frames per second!
My questions are:
Why is this happening? Is it as I think and because simply calling a function is so expensive, or is there something I am missing?
Is there a way that I could still keep my code looking simple without having 20 frames per second?
Update
The key information that I left out was that I was using Xcode playgrounds. I think that this is a bug with SpriteKit and playgrounds. I have filed a bug report with Apple so I’ll see where that gets me.
Speaking about Sprite-kit, I've tested your code to my iPhone 7 in a fresh "Hello world" template :
import SpriteKit
class GameScene: SKScene {
private var label : SKLabelNode?
override func didMove(to view: SKView) {
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.alpha = 0.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
}
func emptyFunction() {}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
//emptyFunction()
//emptyFunction()
//emptyFunction()
//emptyFunction()
//emptyFunction()
}
}
If I don't commented the lines (remove // ) inside the update method, nothing change. I've always 60fps. Check your project to find what are the lines that caused this drastic drop of fps, or if you test your code to a simulator try to a real device. Hope it helps.
Swift has three different methods of dispatch with different performance characteristics:
Direct Dispatch should be very fast. Also known as Static Dispatch.
Table Dispatch is a bit slower due to a method lookup in a witness table. Also known as Dynamic Dispatch.
Method Dispatch is the most dynamic dispatch method. However, it is also the slowest one of the three.
You can force the compiler to use static dispatch by adding final to your method:
final func emptyFunction() {
}
This will also give the compiler additional opportunities for optimisation, such as inlining code. Remember to build with optimisations turned on, which is not the case for debug builds. Therefore you should make sure to choose the release configuration for performance testing. Debug builds of Swift projects are often notoriously slow.
See this post on the Swift blog for more information on method dispatch and the static keyword.
This great post explains the three kinds of method dispatch in Swift and when they are used.
Related
I'm building a Kotlin library to use in my iOS app using Kotlin/Native. After I call some methods in the library from Swift, which works, I also want to call methods in Swift from the library. To accomplish this I implemented an interface in the library:
class Outbound {
interface HostInterfaceForTracking {
fun calcFeatureVector(bitmap: Any?): Array<Array<FloatArray>>?
}
var hostInterface: HostInterfaceForTracking? = null
fun registerInterface(hostInterface: HostInterfaceForTracking) {
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
}
}
This is implemented on the Swift side like this:
class HostInterfaceForTracking : OutboundHostInterfaceForTracking {
var t : Outbound? = nil
init() {
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
func calcFeatureVector(bitmap: Any?) -> KotlinArray<KotlinArray<KotlinFloatArray>>? {
do {
var test : Any? = (bitmap as! Bitmap).bitmap
return nil
} catch {
return nil
}
}
}
The TrackingWrapper looks like this:
class TrackingWrapper : NSObject {
static var instance: TrackingWrapper? = nil
var inbound: Inbound? = nil
var worker: Worker
override init() {
self.worker = Worker()
super.init()
initInboundInterface()
}
func initInboundInterface() {
runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
HostInterfaceForTracking()
}
}
func runOnMatchingLibraryThread(block: #escaping() -> Void) {
worker.enqueue {
block()
}
}
}
The function runOnMatchingLibraryThread is needed because every call to the TrackingLibrary needs to be called from the exact same thread, so the Worker class initializes a thread and enqueues every method to that thread.
The Bitmap in this case is simply a wrapper for an UIImage, which I already accessed with the .bitmap call, so I've tried to access the wrapped UIImage and save it in the test variable. The library gets the current camera frame from the Swift side every few frames and sends the current image wrapped as a Bitmap to the method calcFeatureVector depicted here.
Problem: My memory load starts increasing as soon as the app starts until the point it crashes. This is not the case if I don't access the wrapped UIImage (var test : Any? = (bitmap as! Bitmap)). So there is a huge memory leak, just by accessing the wrapped variable on the Swift side. Is there anything I've missed or is there any way to release the memory?
Looks like you have a circular dependency here:
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
You are asking a property inside HostInterfaceForTracking to maintain a strong reference to the same instance of HostInterfaceForTracking. You should be using [weak self] to avoid the circular reference.
EDIT:
Ok after seeing the rest of you code theres a lot to unpack. There is a lot of unnecessary bouncing back and forth between classes, functions and threads.
There is no need to use runOnMatchingLibraryThread to just create an instance of something. You only need to use that for the code processing the image itself (I would assume, I haven't seen anything so far that requires being split off into another thread). Inside TrackingWrapper, you can create a singleton more easily, and matching the swift pattern by simply doing this as the first line:
static let shared = TrackingWrapper()
And everywhere you want to use it, you can just call TrackingWrapper.shared. This is more common and will avoid one of the levels of indirection in the code.
I'm not sure what Worker or Inbound are, but again these can and should be created inside the TrackingWrapper init, rather than branching Inbound's init, to use another thread.
Inside initInboundInterface you are creating an instance of HostInterfaceForTracking() which doesn't get stored anywhere. The only reason HostInterfaceForTracking is continuing to stay in memory after its creation, is because of the internal circular dependency inside it. This is 100% causing some form of a memory issue for you. This should probably also be a property on TrackingWrapper, and again, its Init should not be called inside runOnMatchingLibraryThread.
Having HostInterfaceForTracking's init, also using runOnMatchingLibraryThread is problematic. If we inline all the code whats happening is this:
TrackingWrapper
init() {
self.runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
}
Having all these classes unnecessarily keep coming back to TrackingWrapper is going to cause issues.
Inside HostInterfaceForTracking 's init, no need to be creating Outbound on a separate thread. First line in this class can simply be:
var t : Outbound = OutBound()
Or do it in the init if you prefer. Either way will also remove the issue of needing to unwrap Outbound before using it.
Inside Outbound you are storing 2 references to the hostInterface instance:
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
I would have imagined there should only be 1. If there are now multiple copies of a class that has a circular dependency, which has multiple calls to separate threads. This again will cause issues.
I'm still not sure on the differences between Swift and Kotlin. In Swift when passing self into a function to be stored, the class storing it would mark the property as weak, like so:
weak var hostInterface: ......
Which will avoid any circular dependency from forming. A quick google says this isn't how things work in Kotlin. It might be better to look into the swift side passing in a closure (lambda on kotlin) and the kotlin side executing that. This might avoid the need to store a strong reference. Otherwise you need to be looking into some part of your code setting hostInterface back to null. Again its a bit hard to say only seeing some of the code and not knowing how its working.
In short, it looks like the code is very over complicated, and needs to be simplified, so that all these moving pieces can be tracked easier.
Is it wrong to call async from Swift object initializer such as this one
let serialQueue = DispatchQueue(label: "com.myApp.SerialQueue")
private let property1:Int?
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
private func setupProperties() {
self.property1 = 1
}
private func nonBlockingSetup() {
//Some non-blocking code that shouldn't run on main thread
}
Some people say async call is problematic before init returns. Need to know what Swift language says about it.
EDIT: Is there any difference if I modify the code as follows:
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
callNonBlockingCodeAsync()
}
private func callNonBlockingCodeAsync() {
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
To answer your question, I tried out the simple example.
Errors are very much self explanatory, in the initialisation process dispatchQueue are capturing self reference right before it's actual initialisation.
You are running into the concurrency problem where initialisation of object is necessary before using it.
dispatchQueue uses closures to provide DispatchWorkItem and as you know closures captures values surrounding it's scope.
Update
One work around would be to give default values to your properties but
I am not sure if that will help you.
In general, a constructor should not do any meaningful work.
Having a constructor that executes code delayed (because it's async) will be unexpected for anyone using that class (quite possibly including you in 6 months), and can therefore lead to bugs. In such cases it's usually better to have a separate initialization method, which makes it clear to an api user that there is something more going on.
If you absolutely want to make sure the initialization method is called, I usually make the constructor private and add a class method for construction. Again this signals api users that there is something going on behind the scenes.
When I try to run the following code on a Simulator (also on a Device, the result is the same) I have a problem: the function executes first the part 2 of the following code (the comments are there just to have a reference, the code itself is entirely in the viewDidLoad. Just to be clear) and then it executes the part 1 (the firebase part). I need the ref.child.... function to be performed before the other function because the "createAlbums" func needs the albums taken from my database (So a completion block would be perfect, but that function doesn't allow me to put any).
I thought about a pair of solutions:
A way to add a completion block (remember that the ref.child... func is like a cycle so it will print the entire "albums" array for every single value that it finds on the database.
Create a Int var and make the 2 part of the code run only if the value of the var is the same as the number of values of "Albums" in the database, so it will fill up the local "Albums" array.
Use two different functions: the the viewDidLoad and the viewDidAppear. As I know, the second one will be performed after the first one.
Which one is the best solution? Is there any better solution?
I also ask you to make suggests on how I can create my own solutions, because I can logically arrive to the point, but I'm not sure to be able to develop them in Xcode.
Just to summarize the entire thread: how can I make the 2 part of the following code run after the 1 part? And why does it run before, even if the order of writing is correct and the compiler runs from the line 1 to the last line?
Thank you for your help and sorry for my bad English, I hope that you can understand everything, unfortunately trying to explain a "complex" problem in English is not always easy.
But I think that the moderators will be great as always to make some corrections!
// 1
let ref = Database.database().reference()
var albums: [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ref.child("Albums").observe(.childAdded) { (snapshot) in
albums.append(snapshot.key)
print("\(albums)")
print("Test print")
}
// 2
createAlbums()
setupConstraints()
}
You'll need to move createAlbums() and setupConstraints() into where you are doing Test Print.
Although it looks like your code is all happening in one place, it's really not.
These lines, between the curly braces...
albums.append(snapshot.key)
print("\(albums)")
print("Test print")
... are happening asynchronously, when firebase calls you back because it has data.
So you need something more like this:
// 1
let ref = Database.database().reference()
var albums: [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
var createdAlbums = false
ref.child("Albums").observe(.childAdded) { (snapshot) in
albums.append(snapshot.key)
print("\(albums)")
print("Test print")
if !createdAlbums {
// Only run this part once, when we
// get our first callback from firebase
self.createAlbums()
self.setupConstraints()
createdAlbums = true
}
}
// 2
}
OTHER WORKING SOLUTION
In that case, observe the .value event on the same reference/query. In your closure, loop over the children as shown here stackoverflow.com/a/27342233, and at the end of the loop call createAlbums().
From a Frank van Puffelen comment
I have 2 delegate methods that are being called by notifications from a 3rd party library.
Method 1:
mediaContentWasUpdated()
Method 2:
adMediaDidBeginPlaying()
In Method 1, a key variable (adDuration) is set from a parameter that is passed in with the notification. As far as I can see this is the only place to get this information.
In Method 2, we check the adDuration and if it is greater than 0 then we update the UI to reflect that we are in fact play an ad.
A bug has appeared where sometimes these two methods are called in the wrong order. Meaning the adDuration is not set and Method 2 thinks there is no ad media to be played and does not update the UI accordingly.
My current attempt at a solution is to make adDuration optional and use an NSCondition to cause Method 2 to wait for Method 1 to set adDuration and then proceed.
var adDuration : Double?
let condition = NSCondition()
func mediaContentWasUpdated(notification: NSNotificiation) {
condition.lock()
if(notificationHasAdDurationInfo(notification)) {
self.adDuration = getAdDuration(notification)
condition.signal()
}
condition.unlock()
}
func adMediaDidBeginPlaying(notification: NSNotification) {
condition.lock()
while adDuration == nil {
condition.wait()
}
if adDuration! > Double(0) {
updateUIForAd()
}
condition.unlock()
}
This is my first time trying something like this and I worry I am doing something wrong. I also have some concerns about locking and unlocking threads needlessly (which would happen in a well timed run, or if there were no ad content to be played).
Outside factors are hindering my ability to test and I wanted to get some input to see if I am heading in the right direction while I wait for those issues to be resolved.
Your discussion of NSCondition got me on the same track with you, and I built two or three solutions using DispatchGroup (which is the better tool for this), but they always had little corner cases that could behave badly, and didn't really capture the intent.
(If you're interested in the DispatchGroup solutions, they're of the form: call .enter() in init, call .leave() when the duration comes in, call notify() when the playing starts. It works fine, but it introduces corner cases that can crash, just like NSCondition.)
Getting back to the real intent:
Update the UI when the duration is known and the ad has started playing.
There's no concurrency going on here. So pulling out GCD is not just overkill; it actually makes things worse because it introduces lots of complicated corner cases.
So I thought about how I'd have solved this back before GCD. And the answer is obvious: just check if you have the data you want, and then do the thing. (Reading through the comments, I see Paulw11 pointed this out as well.)
Personally I like to pull this kind of thing into its own type to make things more self-contained. I hate some of the names here, but the idea should be clear:
class AdPlayer {
private var readyToPlay = false
private var duration: Double = 0.0
private let completion: (Double) -> Void
func setDuration(from notification: Notification) {
if(notificationHasAdDurationInfo(notification)) {
duration = getAdDuration(notification)
}
playIfReady()
}
func play() {
readyToPlay = true
playIfReady()
}
private func playIfReady() {
if duration > 0 && readyToPlay {
completion(duration)
}
}
init(completion: #escaping (Double) -> Void) {
self.completion = completion
}
}
When you set each thing, see if you're ready to update, and if so, update. I've gotten rid of the optional as well, since I believe the intent is "0 duration is always wrong." But you could use an Optional so you could detect actually receiving a 0 from the notification.
With that, you just set up a player property:
player = AdPlayer(completion: updateUIForAd)
(Note that the above might be creating a retain loop, depending on what updateUIForAd is; you may need a [weak self] closure or the like here.)
And then update it as needed:
func mediaContentWasUpdated(notification: NSNotificiation) {
player.setDuration(from: notification)
}
func adMediaDidBeginPlaying(notification: NSNotification) {
player.play()
}
A big advantage of creating the AdPlayer type is that it's easy to reset the system when the ad is done (or if something goes wrong). Just throw away the whole object and create another one.
I am working in between three files: Menu.swift, Main.swift and Game.swift.
In my Main.swift, I define the variable swipeNumber:
class Main {
var swipeNumber: Int = 0 {
didSet{
println("The new swipe number is \(swipeNumber)")
}
}
}
N.B. It is in a class so that I can reference the variable from other files, and the didSet property observer will function.
As you can see, its initial value (I think) is 0.
Then, in my Menu.swift, I retrieve the information from the Main class in Main.swift.
let main = Main()
I then have three buttons, which will, on touch, change the swipeNumber variable, based on which button was pressed.
class Menu: UIViewController {
#IBAction func pressedThreeSwipes(sender: AnyObject) {
main.swipeNumber = 3
}
#IBAction func pressedFiveSwipes(sender: AnyObject) {
main.swipeNumber = 5
}
#IBAction func pressedTenSwipes(sender: AnyObject) {
main.swipeNumber = 10
}
//...
}
When I run the program, my property observer appears to work, printing messages such as:
The new swipe number is 3
The new swipe number is 5
The new swipe number is 10
And in the Game class, (for troubleshooting purposes), I have another property observer, checking the integer of the variable swipeNumber when the button test is pressed:
class Game: UIView {
let main = Main()
func didMoveToView(view: UIView) {
/* Setup your scene here */
println("now")
println("\(main.swipeNumber)"
//Nothing happens here, suggesting that didMoveToView is failing
}
#IBAction func test(sender: AnyObject) {
println("\(main.swipeNumber)")
}
}
My func test prints a number, but sadly that number is not 3, 5, or 10. It's 0.
I think that the problem lies with my variable in Main.swift, however I am not sure.
Any advice or 'fixes', whether quick or lengthy, would be very greatly appreciated.
Thank you,
Will
You have different instances of your class Main, and they each carry a different value for the same properties.
You should try the Singleton pattern (see e.g. here or here).
When you call Main(), you are creating a new object...emphasis on NEW. It has no knowledge of what you've done to other objects of the same type. If you want to use the same object in different places, you need to make it a parameter and pass it into methods rather than creating a different object.