Yet Another Meaningless JavaScript Benchmark

18 01 2010

A while back a friend of mine introduced me to Project Euler which is a “series of challenging mathematical/computer programming problems” all of which should solved by a program that observes the “one minute rule”

“…although it may take several hours to design a successful algorithm with more difficult problems, an efficient implementation will allow a solution to be obtained on a modestly powered computer in less than one minute.”

Even though my primary programming language is C++ I started solving these problems using Perl due to it’s faster development cycle. Another advantage Perl has over C++ (which I didn’t realize was an advantage at the time) is that it’s slow as molasses, especially when used for numeric computations. This comes from the fact that Perl, being a dynamic language, has to deduce the types of objects at run-time, e.g. when you do $i + 1, Perl has to check what type $i is in order to know if it needs to add two numbers or concatenate a number to a string.  So when I got to problem 14 my naive Perl implementation violated the one minute rule. Had I used C++ it would have run in less than three seconds and you would be spared this blog post, but as it was, I tried to think of a way to make it faster.

Problem 14 goes as following:

The following iterative sequence is defined for the set of positive integers:

nn/2 (n is even)

n → 3n + 1 (n is odd)

The problem states that it’s an unproven conjecture that by iteratively feeding the result of the function to itself you’ll eventually reach 1 for all positive integers. The question then is which number (less than one million) has to be fed into the function the most times  before it reaches 1.

My brute force implementation was too slow so I tried to come up with an optimization. Consider the number 3, its chain is:

3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 : length is 8

If we look at 6 the chain is:

6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 : length is 9

Notice that we pass through a number we saw before, there’s no need to recompute the length of 3’s chain, we already know it’s 8. I had to change my implementation to be recursive rather than iterative (so that I will be able to cache the result of all the numbers I pass through).

In order to prove to myself that the difference in speed between C++ and Perl was due Perl’s being a dynamic language I rewrote this in JavaScript (the other dynamic language that I’m somewhat familiar with). My JavaScript implementation lookes like this:

function next(i) {
    if (i & 1)
        return i * 3 + 1;
    return i / 2;
}

function length(n, cache) {
    if (n == 1)
        return 1;

    if (cache !== null && n in cache)
        return cache[n];  

    var ret = 1 + length(next(n), cache);
    if (cache !== null)
        cache[n] = ret;
    return ret;
}

function naive() {
    check(null, "Naive");
}

function optimized() {
    check({}, "Optimized");
}

function check(cache, msg) {
    var max = [0, 0];
    var lim = limit();

    for (var i = 1; i < lim; ++i) {
            var curr = length(i, cache);
            if (curr > max[0])
                max = [curr, i];
    }
    alert(msg + " found " + max[1] + " (chain length: " + max[0] + ")");
}

I then checked this implementation on the three browsers I have installed IE8[1], Firefox 3.5 and Chrome 3 with the following results:

IE took 168 seconds to run the naive solution while Firefox only took 101 seconds but that’s nothing compared to Chrome’s 2.8, that’s right, Chrome’s V8 Javascript implementation is 60 times faster than IE’s, more than an order of magnitude!

The second thing that struck me was how differently the same optimization manifested itself in the three browsers, in both IE and Chrome the optimization shaved off less than half the time (less than 2 fold improvement) while in Firefox the optimized version was 24 times faster than the naive one.

What I assume is happening is that JavaScript on the V8 engine behaves more like a statically typed language because it uses advanced JIT techniques such as trace trees which un-loop recursive function calls and makes (checked) assumptions about the types of variables.

<Edit>

It has been pointed out in comments to this post and on reddit that Firefox’s TraceMonkey uses trace trees while Chrome’s V8 does not. I’m not familiar with this field, I do remember reading about an optimization made by V8 and I thought that it was trace trees but I may have misremembered. In any case it’s pretty clear that V8 uses advanced JIT techniques (although trace trees may not be one of them).

</Edit>

Now I realise that this isn’t a characteristic use of JavaScript, most JavaScript code is not intensively numeric and thus there’s no point in optimizing for this usage. What I find more worrying from the IE perspective is how much better the optimization behaves in Firefox than in IE, this optimization depends on property lookup in objects which is an extremely  common operation in JavaScript. Why there is such a difference between IE and Firefox in this aspect escapes me.


[1] In order to get meaningful results on IE I had to disable the “Stop running this script” dialog





A COM-mon memory leak

27 12 2009

One of the memory leaks I find most often in our code base comes from people being insufficiently lazy when constructing empty BSTRs. That and not understanding/thinking about the low level implementations of using ATL’s wrapper classes.

When using CComBSTRs there are two obvious ways to create empty strings:

CComBSTR empty = L"";
CComBSTR null;

Since there should be no semantic difference between a zero length BSTR and a NULL BSTR I always prefer the second option for two reasons:

  1. It’s 4 less characters to type (six if you use a sane number of spaces)
  2. The first option allocates a new BSTR which includes a count DWORD and a null terminator, not only is this allocation redundant, it may also lead to memory fragmentation.

These reasons can be seen as nitpicking but the real fun starts when CComBSTR’s operator & is used in order to pass the CComBSTR as an out parameter. What operator& does is simply return a raw pointer to the inner BSTR field, this means that when we enter a function that accepts a BSTR* no knowledge of ownership follows the pointer. If this function specified that the BSTR* parameter is an out parameter it will overwrite it without freeing its current value (since the current value may be garbage). If the CComBSTR contained a valid BSTR (e.g. an empty one) this BSTR will be leaked!

A Google code search shows that 5 out of 22 places in which a CComBSTR was initialized with the empty string (L”") go on to use it as an out parameter thus leaking it, most of the other places go on to either assign another value or use Append (which is safe to use on NULL BSTRs so again the empty BSTR is redundant).

ATL’s authors knew this could be a problem and added an assert in operator& that fires if the member BSTR isn’t NULL, however this assert is protected by a macro (ATL_NO_CCOMBSTR_ADDRESS_OF_ASSERT) which isn’t set by default. Why this differs from CComPtr’s operator & (which always asserts) is beyond me.

The root of the problem is that all ATL’s wrapper classes are leaky abstractions, they’re great for saving you some typing here and there and for scoping resource ownership (using RAII) but they don’t save you from thinking about what’s going on behind the scenes. Forget that and the only thing you’ll gain from CComBSTR, CComPtr and friends will be the ability to add bugs faster than before.





Irrationality check

25 11 2009

A recent post from Raymond Chen reminded me of the time when I was in university and I wrote a friend a cheque for Π Shekels.

Wanting to see if it would accepted, we placed it in a deposit box (to nullify the effect of facing the cashier).

I put the Greek letter Π in the numerical field and “פאי” (“Pi”) in the textual field.

Cheque for Pi $

This is a forgery based on a sample from Wikipedia

Several days later my account was debited by 3.14 NIS (~1$), I was a bit peeved that they rounded Pi to only two decimal points but decided it’s as good as I could have expected.

I called the bank and asked for a photocopy of the offending cheque (this was before you could see cheques online), the person taking my call was rather surprised since the fee for ordering a photocopy cheque to be sent was higher than the cheque’s amount.

My friend suggested that our next experiment would be with אo Shekels but I declined.

All this was before I heard of Knuth’s custom of writing cheques for hex-dollars.

This story has a sad story since I lost the photocopy of this cheque one of the times I moved…





The Stroop effect and usability bugs

11 11 2009
Warning: Colour blind people may have difficulty with some of the examples in this post.

I am, for my sins, married to a psychologist.

There, I’ve said it, feels good to get it off my chest.

Before we got married my wife’s ink-jet printer’s colour cartridge got clogged. I held it over a cup of boiling water and then, wanting to see if the remedy worked, I fired up word and typed the following:

Red green blue

“Oh, you’re familiar with the Stroop effect” says SHMBO.

“Nope I’m just a twisted individual” I didn’t say.

Apparently the Stroop effect states that it takes much longer to name the colours of the words (blue, red, green) than read the words (red, green, blue), this happens because reading is so ingrained in our psyche that our brains automatically decipher the text (i.e. read) and we have to make a conscious effort to ignore the meanings of the words.

As an anecdote, being bilingual1 I find naming the colours in Hebrew to be much easier than naming them in English (even though the text is the same).

Fast forward half a decade…

A couple of years ago the the company I work for decided that having support personal communicate with R&D via email caused too much trouble and decided to use an in-house escalation system from a company we recently acquired. Our group was chosen to beta test this system and I was put in charge of the beta program. One day I noticed this graph in my dashboard for SLA violations:

Green is red, Red is yellow and Yellow is violet

I, happy to combine the disciplines of computer engineering and psychology, merrily opened a bug which was just as merrily rejected.

In order to prove that this is in fact a defect I decided to show that Excel had the expected behaviour:

Pie chart in Excel 2007

OK, maybe Excel isn’t a good example, everyone knows that Microsoft don’t know how to write software, I bet Google got this right.

Pie chart in Google docs

Hmmm, that’s even worse, at least Excel got one out of three right…

Lets analyse what someone reading these graphs needs to do.

  1. Look at the graph and hopefully notice that the colours of the slices don’t reflect their category
  2. Go to the legend and read the categories
  3. Note the colour of the category (this is where Stroop comes into play)
  4. Return to the graph and map the colour to its category

As always the first step to a solution is admitting you have a problem and (evidently) not everyone agrees that this qualifies as such. I for one think it’s a usability bug, it may be a low severity bug if you’re writing a general purpose spreadsheet application like Excel or Google docs but if you’re writing a dashboard I would argue it’s pretty high. After all the whole point of having a dashboard is to give information at a glance.

The moral of the story

If you’re creating a UI that assigning colours to arbitrary string values, consider checking if the string is a common colour’s name2, I wouldn’t go so far as to match Celadon, Cerise and their ilk but anything a four year old would recognise should probably be mapped to its actual colour.


[1] Or as my father says, “I used to think I was bilingual until I realized I’m actually semi-lingual.
[2] W3C specifies 16 valid colours for CSS, this may be a good place to start.

 





The First Bug

27 10 2009

I don’t remember the first bug I ever made but as everybody knows [citation needed] half the fun of parenting is living vicariously through our children.

The other day my first born (age 5¾) decided to make a board game.

He had already made the dice (that’s another story), so he got out his crayons and went to work.

Buggy board game

Buggy board game

The rules were pretty simple

  • Boat: move one step forward
  • Guard tower: lose two turns
  • Torch: move back one step less than the last die throw
  • Cannon (that’s the one with lots of red circles): go back to the beginning
  • Etc.

We were playing merrily along when the configuration in the picture came up, two steps away from the boat, roll the die and…

  1. The die lands on 2
  2. Move two steps to boat
  3. Boat moves us a step forward
  4. Land on torch
  5. Torch moves us back one step less than was rolled
  6. Two was rolled so go back one step
  7. Land on boat (goto 2)

Whoops, we’re in an infinite loop! FB was quick to realize what was going on and quickly stopped his old man from executing the infinite loop (ruining all my fun), we arbitrarily chose which of the two locations to stay on an kept on playing.

What really made my day about this was that this was a non-trivial bug, any non-programmer (which includes most 5 year-olds) would not think to look for an infinite loop and even I who suspected such a thing might occur didn’t really see it before it happened (in my defense I wasn’t looking very hard), most flows didn’t expose this “bug” and it’s only by chance that we ran into it on the first time we played the game.

So anyway that’s my son’s first bug, what was yours?





Kicking off yet another blog

27 10 2009

Welcome to what is currently one of the lowest content bogs on the blogosphere, I know that lots of people start blogs and abandon them after one or two posts and there’s a real danger that I will be one of those people however I’ll try to stick with the program.

I don’t really have that much to say and I don’t have that much time to write either so maybe that will even out. I plan to post rather seldom (on the order of once a month) therefore subscribing to my RSS feed will probably not overwhelm you with tons of nonsense (only grams of it).

See you around, Motti.