Thursday, May 28, 2009

VisualVM

I just got a toy to play with tonight :)

Does it matter if immutable data is slow?

Note: this post is specifically about Java.

One thing I noticed about the benchmarks in the blog post I linked to on my last post is that the Java tests weren't wrapped in synchronized blocks (which would be a given in any concurrent program).

So I decided to modify it a bit and see how things went. Here's the code:

public final class Bench {
 public static Object mutex = new Object();
 public static Object getMutex() {
  return mutex;
 }
 //immutable
 private static final class Person0 {
  public final String name;
  public final int age;
  public final double balance;
  
  public Person0(final String name, final int age, final double balance) {
   this.name = name;
   this.age = age;
   this.balance = balance;
  }
 }
 
 //idiomatic mutable
 private static final class Person1 {
  private String name;
  private int age;
  private double balance;
  
  public Person1(final String name, final int age, final double balance) {
   this.name = name;
   this.age = age;
   this.balance = balance;
  }
  
  public void setName(final String n) {
   this.name = n;
  }
  
  public void setAge(final int n) {
   this.age = n;
  }
  
  public int getAge() {
   return this.age;
  }
  
  public void deposit(final double n) {
   this.balance += n;
  }
 }
 
 //mutable w/ public fields
 private static final class Person2 {
  public String name;
  public int age;
  private double balance;
  
  public Person2(final String name, final int age, final double balance) {
   this.name = name;
   this.age = age;
   this.balance = balance;
  }
  
  public void deposit(final double n) {
   this.balance += n;
  } 
 }
 
 private interface Test {
  double test(int iters);
 }

 private static final Test TEST0 = new Test() {
  public double test(int iters) {
   Person0 person = new Person0("John Doe", 19, 100.0);
   for (int i = 0; i != iters; i++) {
    synchronized (mutex) {
     person = new Person0(person.name, person.age + i, person.balance + 1.0);
    }
   }
   return person.balance;
  }
 };
 
 private static final Test TEST1 = new Test() {
  public double test(int iters) {
   Person0 person = new Person0("John Doe", 19, 100.0);
   Person0 p;
   for (int i = 0; i != iters; i++) {
    p = new Person0(person.name, person.age + i, person.balance + 1.0);
    synchronized (mutex) {
     person = p;
    }
   }
   return person.balance;
  }
 };
 
 private static final Test TEST2 = new Test() {
  public double test(int iters) {
   Person1 person = new Person1("John Doe", 19, 100.0);
   for (int i = 0; i != iters; i++) {
    synchronized (getMutex()) {
     person.setName("John Doe");
     person.setAge(person.getAge() + i);
     person.deposit(1.0);
    }
   }
   return person.balance;
  }
 };
 
 private static final Test TEST3 = new Test() {
  public double test(int iters) {
   Person1 person = new Person1("John Doe", 19, 100.0);
   for (int i = 0; i != iters; i++) {
    synchronized (mutex) {
     person.name = "John Doe";
     person.age += i;
     person.deposit(1.0);
    }
   }
   return person.balance;
  }
 };
 
 private static final Test TEST4 = new Test() {
  public double test(int iters) {
   Person0 person = new Person0("John Doe", 19, 100.0);
   for (int i = 0; i != iters; i++) {
    person = new Person0(person.name, person.age + i, person.balance + 1.0);
   }
   return person.balance;
  }
 };
 
 private static final Test TEST5 = new Test() {
  public double test(int iters) {
   Person1 person = new Person1("John Doe", 19, 100.0);
   for (int i = 0; i != iters; i++) {
    person.name = "John Doe";
    person.age += i;
    person.deposit(1.0);
   }
   return person.balance;
  }
 };
 
 private static double test(int times, Test test, int iters) {
  long best = Long.MAX_VALUE;
  double balance;
  for (int i = 0; i != times; i++) {
   long now = System.currentTimeMillis();
   balance = test.test(iters);
   now = System.currentTimeMillis() - now;
   if (best > now) best = now;
  }
  return (double) iters / ((double) best / 1000.0);
 }

 public static void main(String[] args) {
  final int iters = 10000000;
  System.out.printf("Immutable sync all: %f updates/s\n", test(5, TEST0, iters));
  System.out.printf("Immutable sync =: %f updates/s\n", test(5, TEST1, iters));
  System.out.printf("Mutable idiomatic: %f updates/s\n", test(5, TEST2, iters));
  System.out.printf("Mutable fields:  %f updates/s\n", test(5, TEST3, iters));
  System.out.printf("Unsafe immutable: %f updates/s\n", test(5, TEST4, iters));
  System.out.printf("Unsafe mutable:  %f updates/s\n", test(5, TEST5, iters));
 }
}

And the results:

Immutable syncing constructor14,903,129 updates/s
Immutable syncing assignment14,880,952 updates/s
Mutable with idiomatic Java18,832,391 updates/s
Mutable accessing fields directly18,832,391 updates/s
Non-synced immutable70,921,985 updates/s
Non-synced mutable322,580,645 updates/s

Interesting points to note:

  • Immutables turn out to be slightly faster than mutables when synchronization is involved.
  • Using idiomatic set/get methods directly seems to be about as fast as accessing fields directly (albeit more verbose).
  • Non-thread-safe code runs about an order of magnitude faster than the synchronized counterparts. Does that mean I need 20+ CPUs to match the speed of a single-threaded program (due to synchronization overhead)?

So is concurrency really overhyped or did I just do something dumb with the code?

Monday, May 25, 2009

JNA

Looks pretty interesting. Access to C libs from Java, without using JNI.

Friday, May 22, 2009

Dunning-Kruger and Cobra

I saw Cobra the other day and was curious about what people had to say about it. I noticed that some people are rather quick in pronouncing it "retard", "stupid", and some other not-so-flattering adjectives.

It's pretty disappointing that these people are so quick to take a scornful position when a new language comes along and it happens to be, you know, different. Or, (gasp) similar to stuff that already exists.

I guess I'll have to wait a bit more before I get to see comments from people who have actually used the language.

Wednesday, May 13, 2009

Tuesday, May 12, 2009

DDo$

Hilarious and scary at the same time: the Pirate Bay founders are encouraging people to make small payments that incur more fees than the payment is worth to the law firm that represented the music companies in the trial.

The bank account to which the payments are directed has only 1000 free transfers, after which any transfers have a surcharge of 2 SEK for the account holder. Any internet-fee payments made after the first 1000, which includes the law firm’s ordinary transfers, will instead of giving 1 SEK, cost 1 SEK to the law firm.

Saturday, May 9, 2009