BARIIIIIIICODE
BARIIIIIIICODE

Reputation: 133

Optimizing code Physics

I have been trying to optimize code to lower my cpu usage. I have rewritten these functions a couple of times to no avail, looking for some assistance.

63% of time spent on for ball in ... line which only has at most 24 children.

   func totalMass() -> CGFloat {
    var ret : CGFloat = 0
    for ball in self.children as! [Ball] {
        ret += ball.mass
    }
    return ret
}

almost 90% of time spent on the if distance(food...) there can be over 800 pieces of 'food' at one time.

func randomMove() {
        confidenceLevel = 0
        if let b = self.children.first as! Ball? {
            if b.physicsBody?.velocity == CGVector(dx: 0, dy: 0) {
                //print("a")
                self.move(randomPosition())
            } else if b.position.x + b.radius > 1950 || b.position.x - b.radius < -1950 {
                //print("here", b.position.x, b.radius)
                self.move(randomPosition())
            } else if b.position.y + b.radius > 1950 || b.position.y - b.radius < -1950 {
                //print("there")
                self.move(randomPosition())
            } else {
                // Keep moving
                let scene : GameScene = self.scene as! GameScene
                for food in scene.foodLayer.children as! [Food] {
                    if distance (food.position, p2: self.centerPosition()) < b.radius * 5 {
                        self.move(food.position)
                        return
                    }
                }
            }
        }
    }

100% of the time spent on the closing curly brace line }) before the third IF statment

override func didSimulatePhysics() {

        world.enumerateChildNodesWithName("//ball*", usingBlock: {
            node, stop in
            let ball = node as! Ball
            ball.regulateSpeed()
            if let body = ball.physicsBody {
                if (body.velocity.speed() > 0.01) {
                    ball.zRotation = body.velocity.angle() - self.offset
                }
            }
        })

        if let p = currentPlayer {
            centerWorldOnPosition(p.centerPosition())
        } else if playerLayer.children.count > 0 {
            let p = playerLayer.children.first! as! Player
            centerWorldOnPosition(p.centerPosition())
        } else {
            centerWorldOnPosition(CGPoint(x: 0, y: 0))
        }
    }

Upvotes: 0

Views: 85

Answers (2)

Knight0fDragon
Knight0fDragon

Reputation: 16827

Use of the distance formula in this manner can be an expensive operation, If you do not need the actual distance, I would recommend you use distance squared

func randomMove() {
        confidenceLevel = 0
        if let b = self.children.first as! Ball? {
            if b.physicsBody?.velocity == CGVector(dx: 0, dy: 0) {
                //print("a")
                self.move(randomPosition())
            } else if b.position.x + b.radius > 1950 || b.position.x - b.radius < -1950 {
                //print("here", b.position.x, b.radius)
                self.move(randomPosition())
            } else if b.position.y + b.radius > 1950 || b.position.y - b.radius < -1950 {
                //print("there")
                self.move(randomPosition())
            } else {
                // Keep moving
                let bRadiusSqr = b.radius * 5 * b.radius * 5
                let selfPosition = self.centerPosition()
                let scene : GameScene = self.scene as! GameScene
                var moved = false
                scene.foodLayer.children.forEach() {
                    food in
                    if(!moved)
                    {
                        let diffX = food.position.x - selfPosition.x
                        let diffY = food.position.y - selfPosition.y 
                        if diffX * diffX + diffY * diffY < bRadiusSqr  {
                            self.move(food.position)
                            moved = true
                        }
                    }
                }
            }
        }
    }

Going through every food item is a bad way to go, instead give this a try:

func randomMove() {
        confidenceLevel = 0
        if let b = self.children.first as! Ball? {
            if b.physicsBody?.velocity == CGVector(dx: 0, dy: 0) {
                //print("a")
                self.move(randomPosition())
            } else if b.position.x + b.radius > 1950 || b.position.x - b.radius < -1950 {
                //print("here", b.position.x, b.radius)
                self.move(randomPosition())
            } else if b.position.y + b.radius > 1950 || b.position.y - b.radius < -1950 {
                //print("there")
                self.move(randomPosition())
            } else {
                // Keep moving
                let bRadiusSqr = b.radius * 5 * b.radius * 5
                let selfPosition = self.centerPosition()
                let scene : GameScene = self.scene as! GameScene
                let world : SKPhysicsWorld = scene.physicsWorld
                world.enumerateBodiesInRect(CGRectMake(selfPosition.x - b.radius, selfPosition.y - b.radius,bradius*2,bradius*2))
                {
                    body,stop in
                    guard let food = body.node as? Food else { return}                        
                    let diffX = food.position.x - selfPosition.x
                    let diffY = food.position.y - selfPosition.y 
                    if diffX * diffX + diffY * diffY < bRadiusSqr  {
                        self.move(food.position)
                        stop = true
                    }
                }
            }
        }
    }

Unfortunately, our choices are to get bodies in a ray, point, or rectangle, but this will get us all the physics body withing a box of the radius. We use the distance formula afterwards for better accuracy.

Upvotes: 1

Mike Dunlavey
Mike Dunlavey

Reputation: 40659

You know the old saw about the fisherman? Give him a fish, you feed him for a day. Teach him to fish, you feed him for a lifetime.

Rather than tell you what to fix, let me explain how you can find the speedups, by manual sampling.

Let's suppose your code can be made faster (it probably can). If you do it, it will save some fraction of time, like 20%, say. That means the specific detailed activity that you are going to optimize, no matter what it is, is actually in the process of executing at least 20% of the time.

It could be executing a larger fraction of the time, like 40%. It's just that your optimization may not get rid of all of it. You may only get rid of half of it, so you may only net 20%.

That means if you halt it at random, and examine everything it is doing in detail and why at that point in time, there is a 40% chance you will see it doing the thing you are going to optimize, the wasteful thing.

How do you know if what you see, while wasteful, accounts for enough time to bother fixing? You know, if you see it a second time. Seeing something once doesn't tell you much - only that it takes non-zero time, but the second time you see it, you don't know how big it is, but you do know it's not small. The fewer times you sample before seeing it twice, the bigger it is. Here are the statistics.

So if it's going to save 20%, and if it's executing for 40% of the time, how many samples do you need before seeing it twice? On average, 2/.4, which is 5 samples. If you take 10 samples, how many times can you expect to see it? Four. If you take 10 samples, what is the probability you won't see it more than once? 4.5% If you take 20 samples, what is the probability you will miss it? Practically zero.

So this is the power of manual sampling. You can see the precise detail of what the program is doing and why it's doing it. This is what profilers don't tell you. They take way more samples, and then they mush them together into timing numbers, so you can see so-called "hot code", but you're left guessing what you might do to fix it. Manual examination of a small number of samples, in detail, putting all your programmer intellect into understanding each one, tells you exactly.

That's why this post, while counter to common wisdom, has so many votes. You get more speed out of it.

Upvotes: 4

Related Questions