Saturday, December 06, 2008

Silly performance musings: x.toString() vs (String)x

Here is something I have been wondering occasionally:

given an Object that I know to be of type String, is it more efficient to cast it to String, or to call .toString() on it?

Now, if you have C/C++ background, the question may not initially make any sense: isn't cast just syntactic sugar -- compile-time adjustment to offsets -- whereas toString() a virtual method call, relatively quite expensive? Well, no, casts in Java can actually be more complicated lookups through inheritance hierarchy, especially if casting to interfaces (although not as complicated as if Java had Multiple Inheritance). So in that sense, it could be that the method dispatch might be smaller cast. Further, since String.toString() just does 'return this', there is distinct chance it might over all be faster approach.

Now: I am sure that it is hard to find a case where possible performance difference would matter a lot -- both methods ought to be fast enough -- so this is an academic question. But it is easy enough to write code to test it out, so I did just that. If they ever make Trivial Pursuit: the Java micro benchmark edition, I will be so ready...

In thend end, results are surprisingly clear: it is at least twice as fast to cast Object to String than to call Object.toString(). So there you have it. No point in calling toString() if you truly know the type to be String (calling toString() is still useful if it's either String, or some object that renders as String, such as StringBuilder or StringBuffer). C/C++ intuition would have given the right answer, even if not for right reasons.

One interesting sidenote is that this is one case where JDK 1.6 does some funky thingamagic to casts: code is about twice as fast as under 1.4 or 1.5 (especially so for casts, but also for tiString()). Quite neat, whatever it does.

Oh, and the source code: this is extremely ugly; view at your discretion etc. But here goes:


public final class Test
{
    final static int ROUNDS = 999999;

    void test(Object[] args) throws Exception
    {
        boolean foo = false;

        while (true) {
            long now = System.currentTimeMillis();
            String desc;
            int result;

            if (foo) {
                desc = ".toString()";
                result = testToString(args);
            } else {
                desc = "(String)";
                result = testCast(args);
            }
            foo = !foo;

            now = System.currentTimeMillis() - now;
            System.out.println("Type: "+desc+"; time: "+now+" (result "+result+")");
            Thread.sleep(100L);
        }
    }

    int testToString(Object[] args)
    {
        int len = args.length;
        String str = "";
        for (int r = 0; r  ROUNDS; ++r) {
            for (int i = 0; i  len; ++i) {
                str = args[i].toString();
            }
        }
        return str.hashCode();
    }

    int testCast(Object[] args)
    {
        int len = args.length;
        String str = "";
        for (int r = 0; r  ROUNDS; ++r) {
            for (int i = 0; i  len; ++i) {
                str = (String) args[i];
            }
        }
        return str.hashCode();
    }

    public static void main(String[] args) throws Exception
    {
        if (args.length  1) {
            throw new Exception("Need Args!");
        }
        Object[] args2 = new Object[args.length];
        System.arraycopy(args, 0, args2, 0, args.length);
        new Test().test(args2);
    }
}

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.