Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

25 Nov 2008

In JIT we trust, part 2

One of the things I love about dynamic compilation is that it frees me from having to think about the minute details of how some piece of code will be compiled. In fact, the effects of hand-written ‘optimisations’ are less predictable than one might expect!

Take this simple example: which one do you prefer between these two?

  1. for (i = 0; i < array.length; ++i)
  2. for (i = 0, len = array.length; i < len; ++i)

I've been guilty, for a long time, of picking the second one, on the premise that if the length operator didn't have to be called all the time, it'd be faster.

That's the biggest pack of lies ever.


The punchline is that both are about the same in terms of performance, counter-intuitive as that may be. I wrote a little test program, which I tested on my system (Java SE 6, update 10, 64-bit server VM). All it does is print all the command-line arguments to System.out, a million times.

My code tests this loop under a number of different execution scenarios: checking the array boundary the ‘naive’ way (i.e., checking the array length at each run); hoisting the array length and checking that; using for-each (aka extended for loops); as well as hoisting System.out for each of those test types. There's also a ‘super hoist’ mode that does all the above hoisting, as well as doing the million-run loop by hand rather than using a range iterator (see below).

To minimise other system effects, I wrote a null output stream (which acts like /dev/null, without actually opening any device), a range iterator (that always returns null, rather than the iteration count, to avoid boxing overhead), and a no-op loop (just to see how much overhead the loop itself is creating). To minimise warm-up effect, the whole suite is run three times.


Here are some test results for a run with 25 empty arguments. I picked empty arguments to minimise any string-copying overhead that may exist; after all, the point here is to test the various array looping techniques, not string copying!

Timing results (all times in seconds)
Loop typeRun 1Run 2Run 3
Naive11.33810.45510.460
Length-hoisted11.78611.51911.299
For-each11.52111.51711.141
Out-hoisted naive11.41911.46511.184
Out-hoisted length-hoisted11.51411.47511.168
Out-hoisted for-each11.73011.51911.512
‘Super hoist’11.61411.53912.045

If anything could be considered ‘faster’, I'd have to say it's the naive loop! However, the differences are too minute to be meaningful.


Code listing:


import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class PrintArgs {
    static final int RUNS = 1000000;

    public static void main(String[] args) {
        System.setOut(new PrintStream(new NullOutputStream()));
        runSuite(args);
        runSuite(args);
        runSuite(args);
    }

    private static void runSuite(String[] args) {
        time(new NoopLoop(args));
        time(new NaiveLoop(args));
        time(new NoopLoop(args));
        time(new LengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new ForEachLoop(args));
        time(new NoopLoop(args));
        time(new OHNaiveLoop(args));
        time(new NoopLoop(args));
        time(new OHLengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new OHForEachLoop(args));
        time(new NoopLoop(args));
        time(new SuperHoistLoop(args));
        System.err.println();
    }

    private static void time(Runnable runnable) {
        long t0 = System.currentTimeMillis();
        runnable.run();
        long t1 = System.currentTimeMillis();
        System.err.printf("%s: %d ms%n", runnable.getClass().getName(),
                t1 - t0);
    }

    static class NullOutputStream extends OutputStream {
        public NullOutputStream() {}

        @Override
        public void write(int b) {}

        @Override
        public void write(byte[] b) {}

        @Override
        public void write(byte[] b, int off, int len) {}
    }

    static class Range implements Iterable<Void> {
        private final int start, end;

        public Range(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public Range(int end) {
            this(0, end);
        }

        @Override
        public Iterator<Void> iterator() {
            return new RangeIterator(start, end);
        }
    }

    static class RangeIterator implements Iterator<Void> {
        private int start;
        private final int end;

        public RangeIterator(int start, int end) {
            if (start > end)
                throw new IllegalArgumentException();
            this.start = start;
            this.end = end;
        }

        @Override
        public boolean hasNext() {
            return start != end;
        }

        @Override
        public Void next() {
            if (start == end)
                throw new NoSuchElementException();
            ++start;
            return null;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    static abstract class MainRunnable implements Runnable {
        private final String[] args;

        protected MainRunnable(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            for (Object _ : new Range(RUNS))
                main(args);
        }

        protected abstract void main(String[] args);
    }

    static class NoopLoop extends MainRunnable {
        public NoopLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {}
    }

    static class NaiveLoop extends MainRunnable {
        public NaiveLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                System.out.println(args[i]);
        }
    }

    static class LengthHoistedLoop extends MainRunnable {
        public LengthHoistedLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                System.out.println(args[i]);
        }
    }

    static class ForEachLoop extends MainRunnable {
        public ForEachLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                System.out.println(arg);
        }
    }

    static class OHNaiveLoop extends MainRunnable {
        private final PrintStream out;

        public OHNaiveLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                out.println(args[i]);
        }
    }

    static class OHLengthHoistedLoop extends MainRunnable {
        private final PrintStream out;

        public OHLengthHoistedLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                out.println(args[i]);
        }
    }

    static class OHForEachLoop extends MainRunnable {
        private final PrintStream out;

        public OHForEachLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                out.println(arg);
        }
    }

    static class SuperHoistLoop implements Runnable {
        private final String[] args;

        public SuperHoistLoop(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            PrintStream out = System.out;
            for (int i = 0; i < RUNS; ++i) {
                for (String arg : args)
                    out.println(arg);
            }
        }
    }
}

21 Nov 2008

Syntactic sugar makes the world go 'round

For a long time, I considered Java the C of the Java platform. It's pretty much the lowest-level language you can write code with, unless you're using assembly language (and I remember playing with Jasmin in the early days of Java).

Back in early days, the Java language has very little in the way of syntactic sugar, much like C, except that in C you have a preprocessor, whereas in Java, you had to hack your own (just look at the OpenJDK source code for ample examples of custom preprocessing tools in use). So all the code is ultra-verbose, and (to me) there was a huge mental barrier to writing code of any size, just because I had to spell everything out, every time. (Just don't get me started on the lack of typedef in Java.)

With what syntactic sugar there was, it was too easy to encourage programmers to Do The Wrong Thing™: the availability of operator += for strings meant that a lot of programmers tried doing string-building “the C++ way”, never knowing the difference in semantics between += in C++ versus Java. My feeling on this is that += should not be defined for strings, only on StringBuilders and StringBuffers, with the same effect as calling append() but without the function call syntax.

Of course, once we go down that road, perhaps we would need to provide operator overloading across the board. That, of course, leaves me asking why we don't just use C++ in the first place, perhaps doing a variation of C++/CLI for the Java platform. Oh wait, the Java platform isn't as accommodating as .NET is non-Java concepts; I just can't imagine how, say, the STL would be implemented on the Java platform.


As of Java 5, there were certainly a lot more syntactic sugar for things that were commonly done ‘the hard way’ in the past; type-safe enumerations and generics are two that come to mind, not to mention ‘real’ for loops and variadic functions. And soon (perhaps by Java 7, perhaps not), there'll be closures, which I also welcome.

After all these changes, would Java then be seen as the C++ of the Java platform? (Sorry, this is not meant as a slight to C++; I know that Java does not even come close to approaching the power of C++, but it's the best analogy I can come up with.) Is this actually worth pursuing, or should we all start flocking to more powerful languages, perhaps to Groovy?

I singled out Groovy for one reason: it was a language designed for the Java platform, and has the greatest chance of fitting snugly within the Java platform. Of course, there are things it needs to implement, like annotations, but all in good time. My feeling is that, be it Groovy or some other new language, it has to be a language that allows you to express anything you can in Java; and it has to also allow more powerful ways of expressing them, as well as things that cannot easily be expressed in Java.

I would love nothing more than for programmers to write most of their code in a high-level language, with the bottlenecks separated out into a separate module, which can be written in a lower-level language like Java, for performance. But to get people to switch, that high-level language had better be good to use, and not compromise on features that are available in the Java language.


So, the whole point of that little ramble is that I do think there needs to be a ‘better Java’ for Java programmers. Everything that's easy in Java should stay easy, and anything that's commonly done in Java (and programming in general) but is hard should also be made easy, to further encourage their use. Also, it should link to Java in a seamless way; it should be easy to incrementally convert a project to this new language, file by file.

The language can use either static typing (provided there's good type inference so programmers don't have to spell out types all the time) or dynamic typing; I don't care either way, especially given developments in Da Vinci Machine that will give dynamic languages much better support on the Java platform. I have a feeling, though, that experienced Java programmers will continue to prefer static typing.

The language I envision does not need to be backwards-compatible with Java; for example, the whole insane syntax for anonymous classes can be done away with, especially in one-method cases. It also does not need to function in any platform other than the Java platform. I don't really care about portability to unmanaged platforms or to .NET or to Parrot or to anything else.

Really, like I said, I'm just looking for a ‘better Java’ that's good for most present Java programmers to switch to. And, given the current wide(ish) adoption of Groovy, this ideal language would probably be implemented as a future version of Groovy.

16 Nov 2008

Item 52: Refer to objects by their interfaces

(A side related note first: I still need to get the 2nd edition of Effective Java. The 1st edition copy I have is very well-read, though! Highly recommended for all Java programmers. Edit: I did buy the book in the end; InformIT sells non-DRM PDF copies, so yay!)

Anyway, this is a short rant about one of my personal peeves. To put it succinctly, I'll borrow a line out of Effective Java: Item 52: Refer to objects by their interfaces. I believe this so strongly that I've used that as the post title too. :-)

Let's motivate with an example:


Vector<Number> getPrimes(Number from, Number to);
Collection<Number> getPrimes(Number from, Number to);

Which one of the two would you prefer for your interface? Without reservation, I'd prefer the second one, because it doesn't name a concrete return type, thus giving the implementation more flexibility; for example, instead of having to create a Vector, you could use sets, singletons (from Collections), arrays (with a List view created by Arrays.asList), or even a custom type, as long as it fulfils the Collection interface. Clients that want to use Vector could simply construct one from the returned collection.

The same applies to incoming parameters in an interface too. For example:


Number sum(Vector<Number> numbers);
Number sum(Collection<Number> numbers);

Which one do you prefer here? (It could be argued that List should be preferred to Collection if the order of the numbers is significant; however in this example of summing, addition is commutative so this doesn't matter.) To use the first signature would put a burden on your interface's clients; there is no easy construction syntax for making a Vector, whereas on the other hand there are easy ways to express creation of lists (Arrays.asList is variadic), singletons, etc.

In summary, where there are interface types you can use for incoming parameters and return values, you should definitely prefer them. Concrete types are for instantiation only, or perhaps for use in legacy hierarchies that don't have interface types. (For instance, I consider it totally broken that Reader and Writer are abstract classes instead of interfaces.)

14 Oct 2008

More about interfaces than you can throw a HissyFitException at

This post is in response to Mike Stone's Please Hold on the Interfaces.

I believe in judicious use of interfaces. Sometimes it's warranted, such as when it represents concepts that lend themselves to multiple unrelated implementations (e.g., Runnable or Callable).

This is doubly useful in, say, (pre-3.0) GNU C++, where you can cast references of any type to a given interface (or signature as G++ calls it) as long as all members of the interface are implemented. It's much, much more flexible than interfaces as seen in Java or C# (where you have to declare upfront in the implementing class that it's in fact implementing some interface).

Where all implementations are likely to have common functionality, having an abstract class is more sensible, in my opinion. Sometimes (such as in the Java collections framework), having both is sensible, too.


Interfaces do use vtables (in fact all interface method invocations are virtual), so whoever says using interfaces is faster because it avoids virtual calls has never profiled their code before, and worse, has no concept of how high-level source code translates to low-level object code. (Yes, I just downvoted their answer.)

I never see a reason to avoid virtual functions when polymorphism is warranted. Seasoned C++ programmers will tell you that what slows your program down with using virtual functions isn't the indirect function call, but the diminished opportunity for inlining.

However, with a managed platform like Java or .NET, the JIT can easily inline for the common case (profile-guided optimisation for the win!), and deoptimise when the common case is no longer the common case. (For lurkers and archives, what I mean is that if a virtual method is being called mostly through instances of one specific concrete class, that concrete implementation can be inlined into the JITted code until other concrete classes start being used more frequently—in which case the code is re-JITted appropriately.)

In contrast, programmers who think they know where their program's bottlenecks are, without profiling for the common cases, are performing ignorance-guided optimisation.


I don't agree with the "call protected virtual initialisation method from constructor" approach though. Remember that in the superclass constructor, the subclass portion of the object isn't constructed yet, and any such virtual methods you call had better make sure that they only access the superclass portion of the object.

In C++, this is enforced—this is a feature, not a bug. Virtual functions do not exhibit virtual behaviour when called (directly or indirectly) from constructors and destructors. I have some code to demonstrate this:


#include <iostream>

class Base {
public:
    Base() {hw();}
    virtual ~Base() {gw();}

    virtual void println(char const* line) = 0;
    void hw() {println("Hello, world!");}
    void gw() {println("Goodbye, world!");}
};

class Derived : public Base {
public:
    Derived() {}
    virtual ~Derived() {}

    virtual void println(char const* line) {
        std::cout << line << '\n';
    }
};

int
main()
{
    Derived d;
    return 0;
}

Here, even though we're instantiating a Derived object, where the println function is defined, while inside the Base constructor (and destructor) Derived::println is not being used at all.

If the constructor/destructor calls println directly, that will generate a compiler error (since a pure virtual function is being called). In this indirect case, the compilation will go fine, but because Base's vtable is used until execution reaches Derived's constructor, the println() call inside hw() will not work as (naively) expected.

Like I said, this is a feature, not a bug. While the price you pay is that you cannot use virtual behaviour inside constructors and destructors, this actually makes your program safer, by not requiring your virtual functions to be specially coded to only use members in the base class.

So, back to the use of a "protected virtual method" to allow mockable initialisation. The proper way to do this, in my view, is to make an initialize() method, that is called by client code after the object is constructed. Since initialize() isn't being called by the constructor, it means that the object is fully constructed, and that it's safe to call into the appropriate virtual method to do the appropriate type of database initialisation (or whatever).