April 23, 2010

Java Tip #2: Use StringBuilder for String concatenation

And on to the next in a series of Java Tips. While last time the performance gain was very tiny and only affected high-load scenarios, this tip may considerably speed up even simple applications where there is String-processing involved.


Advice

Use StringBuilder instead of + or += to concatenate Strings in non-linear cases (when concatenation not appears immediately one after another), ie. in loops.

Code-Example

Before

  String test = "";
  for(int i = 0; i < 50000; i++) {
     test += "abc";
  }

After

  StringBuilder test = new StringBuilder();
  for(int i = 0; i < 50000; i++) {
     test.append("abc");
  }

Benefit

Huge performance gain! The unoptimized code forces Java to create new Strings and copy the contents around all the time (because Strings are immutable in Java). The optimized code avoids this creation/copying by using StringBuilder. While the Java compiler can optimize linear concatenations, ie.

String test = "a" + "b" + "c";

into using a StringBuilder internally, it is not clever enough to apply this optimization correctly if there is more logic than just concatenation involved. Even if the concatenation is the only operation inside some loop-logic, the Java compiler falls back into creating a whole lot of String (or to be more exact: StringBuilder) objects, copying them around like crazy and causing a huge performance impact in some cases. I confirmed this example for measurement of StringBuilder performance: concatenating 50000 times "abc" in a loop takes ~11000ms, using StringBuilder keeps the time spent at near 0ms. (I tried concatenating with 500000 iterations, but stopped the execution of the first loop after ~10 minutes running unfinished, so the impact is nonlinear. Second loop finished in 15ms with 500000, for the record.)

Remarks

It is not necessary to optimize code like

String email = user + "@" + domain + ".com";

as javac is clever enough for these cases and it would reduce readability considerably. But even with the simplest loops involved the conditions change. For example, internally the first loop gets compiled by javac into following bytecode

 13  new java.lang.StringBuilder [24]
 16  dup
 17  aload_1 [test]
 18  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [26]
 21  invokespecial java.lang.StringBuilder(java.lang.String) [32]
 24  ldc <String "abc"> [35]
 26  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
 29  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [41]
 32  astore_1 [test]
 33  iinc 4 1 [i]
 36  iload 4 [i]
 38  ldc <Integer 50000> [45]
 40  if_icmplt 13

where for each iteration of the loop(13-40) a new StringBuilder is instantiated and initialized with the result of the previous' loop StringBuilders value before appending the constant String just once each time. The optimized code results in this bytecode

 81  new java.lang.StringBuilder [24]
 84  dup
 85  invokespecial java.lang.StringBuilder() [62]
 88  astore 4 [builder]
 90  iconst_0
 91  istore 5 [i]
 93  goto 107
 96  aload 4 [builder]
 98  ldc <String "abc"> [35]
100  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
103  pop
104  iinc 5 1 [i]
107  iload 5 [i]
109  ldc <Integer 50000> [45]
111  if_icmplt 96

in which the loop(96-111) just appends the constant String to the same StringBuilder each time which was created only once before the loop started. No copying around and creation of additional objects necessary, thus a huge performance-gain.

April 18, 2010

My first steps with Eclipse Mylyn

This is my long promised posting about my experiences with Eclipse Mylyn. It took quite some time because I did not stick to it all the time but somehow fell back to my old development-habbits where everything and much more was visible on the screen. Maybe this is caused by the always-changing project situation or changing technologies but I cannot completely blame it to that. Nevertheless, let's focus on my experiences :)

Mylyn works by hiding everything from your visual workspace which is not connected to your current task at hand. This means, you only see the files in the Package Explorer and all other many views which were touched during your current task. This also means that you have to create tasks for your work in the Task List, which is a good idea anyway. The Mylyn-filtering is activated in the moment when you click on the small circle next to such a task in the Task List. If you have worked before on that task, your previous state of the workplace (which is called Context here) gets restored, like the open files, the touched methods and landmarks. If this is a fresh task, you're presented with empty views all around. From here you then can open your initial file to work on either by using the "Open Resource" shortcut or by ALT-clicking into the Package explorer, where the hidden files are displayed then.

From that initial file on you just navigate through the structures and methods with your usual Eclipse navigation (F3, STRG-click, or whatever) and Mylyn takes care to just display and highlight the necessary information for your work. It even cleans up the stuff and removes old and never-again places if you didn't came back to them for a longer time. The same thing applies to all content-assists like the method-overview or all of the Find-shortcuts where the list gets two-parted with your context-relevant results at the top and only the remaining hits after a separator.

You can also mark certain methods or files as "Landmarks" which are then displayed in bold text in your Outline or Package Explorer. This is especially handy if you're hunting a bug and found important places you want to remember or where you nailed down the cause but will fix it at a later time.

I also tried to connect my Mylyn Task List with a local installation of Bugzilla to better manage my tasks and make them available even outside of my own workspace. This worked quite good. It's also possible to attach the Context to each task if you're working with an external task repository. This would allow other people on the same repository to open the Context in the same state as it was when you last saved it to the task repo. Easy moving tasks from one person to another :) But we didn't come around to test this, as I've been the only one to work with Mylyn longer than just for a short tryout.

All in all I can say that if you get used to the way how Mylyn hides everything unnecessary from your display and how it presents the important information, the speed of development really goes up and the amount of distraction minimizes. But to get to that point you have to really work through the initial get-used phase which can be somewhat confusing and make you switch back to the previous development-mode where everything is displayed. I also fell into this a few times. But if you stick to it, you'll soon get the benefit of being able to focus!

If you also want to try it out or are just curious how it feels to work with it, take a few minutes and watch the Eclipse Mylyn 3.0 webcast video and read through the Mylyn 2.0 Tutorial (still applies 100% even if it's been written for 2.0) how to get started, you won't regret it!

April 17, 2010

Java Tip #1: Use StringBuilder instead of StringBuffer

As promised last time I'm presenting here the first in a series of Java Tips. To start easy I'll begin with one of the simplest tip to implement.


Advice

Use StringBuilder instead of StringBuffer.

Code-Example

Before

  StringBuffer str = new StringBuffer();
  str.append("foo");
  String x = str.toString();

After

  StringBuilder str = new StringBuilder();
  str.append("foo");
  String x = str.toString();

Benefit

Small performance gain. StringBuilder is a 1:1 drop-in replacement for the StringBuffer class. The difference is that the StringBuilder is not thread synchronized and therefore performs better on most implementations of Java. Even javac internally translates String x = "foo" + "bar"; into a StringBuilder operation (confirmed with Java 1.6.0_18). A small testcase

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    StringBuffer buffer = new StringBuffer();
    for(int i = 0; i < 1000000; i++) {
        buffer.append("abc");
    }
    System.out.println("Time lapse using StringBuffer: " + (System.currentTimeMillis() - start) + " milliseconds");

    start = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder();
    for(int i = 0; i < 1000000; i++) {
        builder.append("abc");
    }
    System.out.println("Time lapse using StringBuilder: " + (System.currentTimeMillis() - start) + " milliseconds");
}

using Java 1.6.0_18 shows that StringBuilder is roughly 100% faster than StringBuffer. Although on my machine this testcase keeps below 100ms for both with one million appends so this won't be the magical solution for most of your performance issues.

Remarks

Even if this tip can be applied in 98% of all cases one has to be sure that the String is used afterwards and the StringBuilder instance does not get passed between several threads. Since StringBuilder is not thread-safe this would be the only case where StringBuffer should be used. And of course it's not the silver performance bullet but just one slice in string-heavy applications.

April 9, 2010

Recovering slowly and preparing Java Tips

Over the last ten (or so) days, just right for Easter, my health status went down again. I cought a small cold, not enough to keep me from working but bad enough so that I spent my non-work time in bed to recover faster. This helped somewhat in the way that it did not get worse anymore and today I'm not really feeling much of that issue anymore.

But this recovering was paid expensive. My spine aces returned, most probably caused by the lying-in-bed and limited exercising because of my injured ankle. Currently it's bad, not as bad as back in 2006, but still enough so that it causes me a constant pain in the lumbar spine. For now I just can fight this with pain killers but I know I need to get back to exercising as fast as possible to strengthen my muscles in this area again and get stabilized. Of course, I have to pay attention to the possible remainings of the cold and that I do nothing which could harm my wounded ankle.

Totally unrelated to this, some time ago I began collecting the different improvements I make to our sourcecode at work. I did this because I recognized, that I often applied the same improvements over and over again to other people's sourcecode and a lot of them are quite similar to each other. Most of this improvable code exists, because people just don't know that similar functionality already exists in common frameworks and so algorithms and operations get reingeneered again and again.

I plan to develop this collection so that each improvement has a before-after-example, a detailled explaination why this improvement is better, maybe a categorization into different areas (eg. Collections, Framework, Java5-Enhancement, etc.) and a difficulty-level. Maybe if this turns out well, I could also organize an internal training-event to spread this knowledge and raise the quality of our code just a tiny bit :)

Nevertheless, I'm also thinking of publishing each tip here in the blog for free availability and open discussion, so stay tuned if you're a Java developer, maybe there's also something in for you.

April 1, 2010

Multitouch for Windows with Synaptics Touchpads

There hasn't been much of change lately as I'm still busy with getting my ankle healed (it doesn't hurt anymore if I don't tilt the foot unnaturally) and trying to get the schedules and work done for university.

So as a small ping again I'd like to re-blog something which has improved the work with my laptop in the past few days. To be exact, this blog posting gave me the hint that almost all Synaptics-Touchpads used in laptops can be upgraded to recognize multitouch gestures. So it's possible now, as on Macs, to tap two fingers onto the touchpad and automatically engage scrolling mode.

Everything which is needed is to install this updated Synaptics driver for Windows XP (this one for Windows Vista/7) and enable the paging with two fingers in the driver settings.

If you want to know more about the possible actions with the new multitouch (like ChiralScroll), have a look at to the Synaptics gesture descriptions.