Understanding the cost of different functions

So this is something I’ve never been sure of, but since I have a concrete example right now, I figured I’d run it by you all so I can learn for the future.

Basically, I’m making a submarine game, and the submarine returns to a neutral position if no button is pressed. As it stands right now, I check every frame to see if a button is being pressed, and if it isn’t, I Lerp between the current position and the neutral position, like so:

if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
    currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
}

However, this feels wasteful to me, as it will continue to Lerp even when it’s returned to a neutral position. I was considering adding an if statement to check to see if it’s at a neutral position, something like this:

if Mathf.Abs (currentX > .01f){
    if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
        currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
    }
}

Alternately, I could do it:

if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
    if Mathf.Abs (currentX > .01f){
        currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
    }
}

So I guess my question is, which of these 3 would be the fastest? Thanks!

Adam

The difference is quite insignificant with modern technology. And since the calculation is only executed once per frame and not many times for many objects, you could probably just skip this thought. But since you asked, and I was curious myself, I decided to run a test. Like most technical problems, the answer will be… it depends. Depends on the hardware, what value currentX is, if you’re pressing any keys, etc…

To run your own tests, get the profile script here: http://wiki.unity3d.com/index.php/Profiler

Now according to my super-scientific research, the results show that the 3rd method is the fastest on average.

no keys being pressed, currentX being greater than 0.01 for most of the time

  • Method 1 took 0.003008700 seconds to complete over 1000 iterations, averaging 0.000003009 seconds per call
  • Method 2 took 0.001624600 seconds to complete over 1000 iterations, averaging 0.000001625 seconds per call
  • Method 3 took 0.000533800 seconds to complete over 1000 iterations, averaging 0.000000534 seconds per call

Holding up arrow key

  • Method 1 took 0.003370300 seconds to complete over 1000 iterations, averaging 0.000003370 seconds per call
  • Method 2 took 0.002678900 seconds to complete over 1000 iterations, averaging 0.000002679 seconds per call
  • Method 3 took 0.001618500 seconds to complete over 1000 iterations, averaging 0.000001619 seconds per call

Now having said all that, I would personally choose the method that is easiest to read and maintain. Code readability is far superior than micro-optimizations. That doesn’t mean I completely disregard performance, but something as trivial as this seems to be a waste of time and energy that could be focused on other tasks, or be optimizing something that would have a noticeable effect on performance.

Here’s the test script I used:

using UnityEngine;

public class SomeProfileTest : MonoBehaviour {
	
	float currentX = 1000f;
	int iterations = 1000;
	int elapsedIterations = 0;

	// Update is called once per frame
	void Update () {

		float frameCurrentX = currentX;

		Profile.StartProfile("Input Method 1");
			if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
				currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
			}
		Profile.EndProfile("Input Method 1");
		currentX = frameCurrentX;

		Profile.StartProfile("Input Method 2");
			if (Mathf.Abs (currentX) > .01f){
				if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
					currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
				}
			}
		Profile.EndProfile("Input Method 2");
		currentX = frameCurrentX;

		Profile.StartProfile("Input Method 3");
			if (!Input.GetKey (KeyCode.UpArrow) && !Input.GetKey (KeyCode.DownArrow)) {
				if (Mathf.Abs (currentX) > .01f){
					currentX = Mathf.Lerp (currentX, 0, Time.deltaTime);
				}
			}
		Profile.EndProfile("Input Method 3");


		elapsedIterations++;
		if (elapsedIterations >= iterations)
		{
			Application.Quit();
			Debug.Break();
		}
	}
	
	void OnApplicationQuit () {
		Profile.PrintResults();
	}

	void OnGUI() {
		GUI.Label(new Rect(0f, 0f, 100f, 100f), currentX.ToString());
	}
	
}

I don’t think the difference between those would really be noticable, so I wouldn’t bother with trying to figure out the most optimized method. Instead write the code in the way that you think makes the most sense.

If you want to check for a neutral position, then, imo, that’s the last one - I also suspect checking Mathf.Abs() takes a little more to check than 2 input booleans and that way lets you get out of the if statment before you check Mathf.Abs() - I’m not sure about that but I would be surprised if it was different.

I don’t think checking for it makes any real difference performance-wise though, Lerp should be a fairly cheap method.

In fact, not checking may be the best way performance-wise. I don’t know how Lerp compares to (x > Math.Abs), but in cases where you need to Lerp you’ll have to evaluate both. If you only very rarely DONT Lerp (wether this is the case depends on your game), constantly checking for a neutral position might be a net decrease in performance.

Not a definitive answer to your question as to what is fastest I suppose, but some things worth considering in any case.