Today I’m revisiting an article I wrote last August about conditionals: if-else chains, ternary (? :) operators, and switch statements. In that article I showed that if-else chains are about as fast as ternary operators and that both of them are 10-15% faster than switch statements. Today we’ll take a look at how those conditionals scale beyond just the few cases in the last article.

To test how each approach (if-else, ternary, switch) scales, I began with the test application from the previous article and made a few modifications:

  • Increased the number of cases from 5 (0..4) to 20 (0..19). Almost all conditionals have fewer than 20 cases.
  • Based on the findings in Activation Objects, I moved the log log function out of the test function and made it a private field
  • Switched ternary style based on a comment by whitered. This was very helpful as the nesting got deeper.
  • Removed a lot of curly braces from the if-else chain to save room without sacrificing readability

Let’s see how the performance test looks now that those modifications have been applied:

package
{
	import flash.text.*;
	import flash.utils.*;
	import flash.display.*;
 
	public class ConditionalsTestFollowup extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg+"\n"); }
 
		public function ConditionalsTestFollowup()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var beforeTime:int;
			var afterTime:int;
			var ifElseTime:int;
			var ternaryTime:int;
			var switchTime:int;
			var i:int;
			const ITERATIONS:int = 50000000;
 
			log("Iterations,If-Else,Ternary,Switch");
 
			for (var val:int = 0; val < 20; ++val)
			{
				beforeTime = getTimer();
				for (i = 0; i < ITERATIONS; ++i)
				{
					if (val == 0) func0();
					else if (val == 1) func1();
					else if (val == 2) func2();
					else if (val == 3) func3();
					else if (val == 4) func4();
					else if (val == 5) func5();
					else if (val == 6) func6();
					else if (val == 7) func7();
					else if (val == 8) func8();
					else if (val == 9) func9();
					else if (val == 10) func10();
					else if (val == 11) func11();
					else if (val == 12) func12();
					else if (val == 13) func13();
					else if (val == 14) func14();
					else if (val == 15) func15();
					else if (val == 16) func16();
					else if (val == 17) func17();
					else if (val == 18) func18();
					else func19();
				}
				afterTime = getTimer();
				ifElseTime = afterTime - beforeTime;
 
				beforeTime = getTimer();
				for (i = 0; i < ITERATIONS; ++i)
				{
					val == 0 ? func0() :
					val == 1 ? func1() :
					val == 2 ? func2() :
					val == 3 ? func3() :
					val == 4 ? func4() :
					val == 5 ? func5() :
					val == 6 ? func6() :
					val == 7 ? func7() :
					val == 8 ? func8() :
					val == 9 ? func9() :
					val == 10 ? func10() :
					val == 11 ? func11() :
					val == 12 ? func12() :
					val == 13 ? func13() :
					val == 14 ? func14() :
					val == 15 ? func15() :
					val == 16 ? func16() :
					val == 17 ? func17() :
					val == 18 ? func18() :
					func19()
				}
				afterTime = getTimer();
				ternaryTime = afterTime - beforeTime;
 
				beforeTime = getTimer();
				for (i = 0; i < ITERATIONS; ++i)
				{
					switch (val)
					{
						case 0: func0(); break;
						case 1: func1(); break;
						case 2: func2(); break;
						case 3: func3(); break;
						case 4: func4(); break;
						case 5: func5(); break;
						case 6: func6(); break;
						case 7: func7(); break;
						case 8: func8(); break;
						case 9: func9(); break;
						case 10: func10(); break;
						case 11: func11(); break;
						case 12: func12(); break;
						case 13: func13(); break;
						case 14: func14(); break;
						case 15: func15(); break;
						case 16: func16(); break;
						case 17: func17(); break;
						case 18: func18(); break;
						default: func19();
					}
				}
				afterTime = getTimer();
				switchTime = afterTime - beforeTime;
 
				log(val + "," + ifElseTime + "," + ternaryTime + "," + switchTime);
			}
		}
 
		private function func0(): void{}
		private function func1(): void{}
		private function func2(): void{}
		private function func3(): void{}
		private function func4(): void{}
		private function func5(): void{}
		private function func6(): void{}
		private function func7(): void{}
		private function func8(): void{}
		private function func9(): void{}
		private function func10(): void{}
		private function func11(): void{}
		private function func12(): void{}
		private function func13(): void{}
		private function func14(): void{}
		private function func15(): void{}
		private function func16(): void{}
		private function func17(): void{}
		private function func18(): void{}
		private function func19(): void{}
	}
}

I compiled this with MXMLC 4.1.0.16076 and ran it with the release version of the Flash Player 10.1.102.64 plugin. I then ran the test on a 2.4 Ghz Intel Core i5 with Mac OS X 10.6 and a 2.8 Ghz Intel Xeon W3530 with Windows 7. The result is a very large block of numbers that are hard to parse through, so here they are in graph form:

Mac OS X 10.6 results
Windows 7 results

As you can see, the performance of all three conditionals scales linearly. This means that if-else and the ternary operator continue to outperform switch statements by 10-15% on both platforms regardless of how many cases there are or which case is actually matched. This is quite disappointing for switch as it should beat its competition with larger numbers of cases, but sadly it is always the loser for reasons described in the previous article.

Bottom line: beware of switch statements in performance-critical code!