The Kindle is Here!

I finally broke down and bought a Kindle.  I've been eyeballing them since the first one came out and have been daydreaming about them since the Kindle 2 pics first leaked.  After reading countless previews and reviews and raves and rants, I decided it was time.  Sure, it's expensive.  Yeah, it's "only a single function device."  blahblahblah.  The fact is I love to read, I live in a NYC apartment, and I already have an entire wall devoted to bookshelves crammed full of my books.  There's just not that much space here to keep buying more and more books.  Any my library hardly ever has what I want on the shelf.  When they have it at all, there's a waiting list.  So this makes a lot of sense for me in a number of ways.  I've had it for about an hour now so I don't have any deep dive experience with it as such, but as far as first impressions go, it's a big win.  And the first thing I did after browsing through the user's guide?  I bought Brandon Sanderson's latest book "The Hero of the Ages." Now if only I didn't have to work.

String Concatenation Revisited

I had intended to do some follow up numbers to my previous post but I got a bit sidetracked by work and the like.  My simple tests all work with one String that's created then thrown away.  This test helped me resolve the question I had when I started down that road but stops short of a more general answer.  Then I saw this pingback which led me here.  There's some nice analysis and insights to consider.  So given the shortcomings of my little benchmark and the comments there, I wanted to expand my test a bit and see what things look like when the loop doesn't throw away the data.  The test is simple enough again:

import java.util.*;
import java.text.*;

public class ConcatenationTest {
	private static long concat(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = "Loop " + x + " of " + count + " iterations.";
		}
		return System.currentTimeMillis() - start;
	}

	private static long append(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = new StringBuilder("Loop ")
				.append(x)
				.append(" of ")
				.append(count)
				.append("iterations.")
				.toString();
		}
		return System.currentTimeMillis() - start;
	}

	private static long concatAcrossLoops(int count) {
		long start = System.currentTimeMillis();
		String s = "";
		for(int x = 0; x < count; x++) {
			s += "Loop " + x + " of " + count + " iterations.";
		}
		long time = System.currentTimeMillis() - start;
		System.out.println("concatAcrossLoops time = " + time);
		return time;
	}

	private static long appendAcrossLoops(int count) {
		long start = System.currentTimeMillis();
		StringBuilder s = new StringBuilder();
		for(int x = 0; x < count; x++) {
			s.append("\nLoop ")
				.append(x)
				.append(" of ")
				.append(count)
				.append("iterations.");
		}
		long time = System.currentTimeMillis() - start;
		System.out.println("appendAcrossLoops time = " + time);
		return time;
	}

	public static void main(String[] args) {
		int count = 10000;
		List concats = new ArrayList();
		List appends = new ArrayList();
		List concatsAcross = new ArrayList();
		List appendsAcross = new ArrayList();
		for(int x = 0; x < 10; x++) {
			concats.add(concat(count));
			appends.add(append(count));
			concatsAcross.add(concatAcrossLoops(count));
			appendsAcross.add(appendAcrossLoops(count));
		}

		String header = "concats   appends   concats across loops   appends across loops";
		String format = "%7d %9d %22d %22d\n";
		System.out.println(header);
		for(int x = 0; x < 10; x++) {
			System.out.printf(format, concats.get(x), appends.get(x), concatsAcross.get(x), appendsAcross.get(x));
		}
	}
}

And then the results:

concats appends concats across loops appends across loops
48 14 18990 1276
27 11 14581 1442
4 4 13206 1253
3 3 13478 1438
4 4 12651 1444
4 3 12485 1403
4 3 12608 1318
4 3 13152 1312
3 4 12535 1390
4 3 12444 1329

Notice after the first two loops the numbers for all runs drops. As the JIT compiler kicks in, we get some optimization but as you can see concatenation across loop iterations is incredibly much more expensive. In this case, StringBuilder is still the clear winner.

update There was a typo in the original test. I was calling toString() in the appendsAcrossLoop test which was entirely unnecessary. (I forgot to remove that call when adapting from the earlier iteration.) The new results are below. I included them here rather than just replacing the table above as it shows just how expensive that toString() is.

concats appends concats across loops appends across loops
42 15 16562 4
5 8 12564 5
4 3 11601 2
4 2 11141 2
4 3 11025 3
3 3 11260 3
3 3 11062 3
3 3 11738 2
4 2 11078 2
4 2 11130 3

Isn't it time for GCJ to die?

What's the motivation for gcj these days?  Originally, everyone wanted a GPLd JVM so gcj kinda made sense.  At least in spirit.  It's never been a functional equivalent for an actual JVM, though.  I've seen nothing but problems with it for years in IRC channels.  It's partial implementation of the spec has led to endless confusion for uncounted newbies coming to the java channel for help.  It doesn't help that the ideologues at Debian, et. al, continue to package gcj as if it were java.  Well, we have a  GPLd JVM now.  Everything about it is open source (or just about done...). GCJ, as I see it, serves no more useful purpose than allowing those in charge of it to hold on to some ideal (or maybe pride).  I know this is inflammatory for a good number of people, but why persist?  Is it the native compilation you like?  The slow, misbegotten catastrophe that it is?  It's slower than running java bytecode and seems to eliminate several key features of Java (like dynamic classloading).  Even before Sun GPLd their (our?  I'm a Sun guy after all...) JVM, gcj adoption was miniscule at best.  So, what's the point?  Can't we move on from gcj?  Or at a minimum, stop packaging it as the default JVM when it's not actually a java implementation?  That'd work for me.  I'm just tired of seeing newbies getting tripped up by some distro's ideological navel gazing.