Posts Tagged ‘android’

Is a hash faster than a Switch in JavaScript?

Tuesday, August 17th, 2010

I stumbled across this concept recently and I thought I’d share it, because I don’t generally see this pattern being used. More importantly, I also share test results that show that maybe it’s not always a good idea to use this pattern…

The problem with Switch statements

The basic switch statement in JavaScript looks something like this:

var foo = 'c';

switch(foo) {
  case 'a':
  break;

  case 'b':
  break;

  case 'c':
  break;

  default:
}

So what’s wrong with this? The JS engine has to examine a bunch of unrelated cases until it finds the relevant one, executes the code, then breaks out of the switch because the job is done (this is why it’s important to break!). In the above example we had to go through case A and case B until finally reaching case C. What’s worse is that if it didn’t match any of these cases, the JS engine has to jump through ALL of the cases before it reaches Default, the fall-through case.

Actually it’s not so bad, as long as there are a limited number of cases. It’s probably no big deal if you only have a few cases to jump through. The problem gets bigger as your number of cases increases (some of you may know this as O(n)). What happens when there’s 10 cases? Then there’s potentially 10 checks on cases (assuming what ended up being executed was the default). 100 cases? Then potentially 100 checks.

What would be better is if there were a way to reduce the number of checks. One way would be to put the most frequently used cases at the top. This would alleviate some of the pain, but you still end up with extra processing while the JS checks each case. It would be ideal to avoid this extra processing altogether.

An alternative: The hash table

There is a way to avoid this extra processing! It’s by leading the code directly where it needs to go, without unnecessary checking of unrelated cases.

You can do this using a hash. In JavaScript we accomplish this with an object:

var foo = 'c';
var cases = {};
cases['a'] = function() {
  alert('I am A!');
};
cases['b'] = function() {
  alert('I am B!');
};
cases['c'] = function() {
  alert('I am C!');
}

if(typeof cases[foo] == 'function') {
  // only executes if we've defined it above
  cases[foo]();  // I am C!
} else {
  // default (the fallthrough)
}

There we go! No extra case checking here. We’ve led the JS straight to the code we want to execute!

Performance improvement…?

So.. this hash lookup seems faster in theory, but what about in practice? Unfortunately I ended up with some mixed results…

I created a simple performance test on jsperf.com and got these results:

Browser Switch ops/sec Hash table ops/sec % Difference
Chrome 6.0.490.1 dev 34,606,469 43,329,587 25% faster
Safari 5.0 16,777,216 10,854,824 35% slower
Opera 10.61 4,405,782 2,719,336 38% slower
Firefox 3.6.3 2,785,802 2,400,586 14% slower
IE6 147,870 206,869 40% faster
IE7 144,735 191,179 32% faster
IE8 350,085 472,417 35% faster
Mobile Safari (iOS4 on iPhone 3GS) 668,053 416,366 37% slower
Android (2.2 on Nexus One) 605,693 864,591 42% faster

* Ops/sec = Operations per second. Higher is better
** Chrome, Safari, Opera, and Firefox were tested on Mac OSX 10.6.4 2.53GHz Intel Core i5. IE tests were run on Windows 7 64bit 2.4GHz Quad Core

The Results

From the results, it looks like the hash optimization is only a benefit for Chrome, IE6-IE8, and Android. That’s quite a specific sampling. My guess is that the other browsers have implemented some sort of Switch statement optimizations that actually turn the hash optimization into an antipattern.

More info

Although I first read about this online, by no surprise this trick also appears in Nicholas Zakas’s High Performance JavaScript in a section on “Lookup Tables” (p. 72).

How to install obsolete Android Virtual Devices (AVDs)

Wednesday, June 9th, 2010

Apparently it’s no longer possible to easily download Android versions 2.0 and 2.0.1 from the AVD Manager. I noticed this problem when I got a new machine and had to install everything from scratch. In the future I suspect even more AVDs will be made obsolete, so this solution also applies to them.

Why would you want to install obsolete AVDs? In my case it’s a matter of research: I simply want to see the progress of features being added to its browser, and track those changes over time.

Step 1: Manually download and inspect repository.xml

When updating from “Available Packages” there’s a little one-line error saying “Some packages were found but are not compatible updates.”

Ok, so let’s check out the XML for ourselves to see if we can find anything. So point your browser to https://dl-ssl.google.com/android/repository/repository.xml (to see the XML, right click to view the page source if you’re using a Webkit-based browser).

Search for the AVD version you want. In this case we want 2.0 and 2.0.1, so a simple search find the relevant blocks of code. And we also find the XML tag that’s the cause of our troubles, which prevents us from easily getting the AVDs:

<sdk:obsolete />

Step 2: Get the AVDs!

At this point you could do two things, either save repository.xml to your computer and remove these “obsolete” tags (then add the XML to your AVD Manager by clicking “Add Add-on Site…”), or simply find the path to the AVD and download it manually.

The second option is to simply manually download the paths, which are easy to find in the XML and are listed here for your convenience:
Android 2.0 AVD (Linux)
Android 2.0 AVD (Mac OSX)
Android 2.0 AVD (Windows)

Android 2.0.1 AVD (Linux)
Android 2.0.1 AVD (Mac OSX)
Android 2.0.1 AVD (Windows)

Step 3: Install!

Create directories under your Android SDK installation’s “platforms” folder. In this case we have 2.0 (API level 5) and 2.0.1 (API level 6), so I created these folders: android-5 and android-6. Now just unzip the contents into these folders.

Start up the AVD Manager and click on “Installed Packages”. If you don’t see your new (obsolete) packages you just installed, hit the Refresh button and you should see them.

Now you can create new AVDs with these obsolete packages!