#626 FanInt.java performance tuning

qualidafial Fri 5 Jun 2009

In FanInt.java, the isSpace, isAlpha and isAlphaNum methods use a try..catch block to optimize for a rare case. But, isn't just setting up the exception handler a higher performance penalty than the corresponding integer comparison in the existing if statement?

brian Fri 5 Jun 2009

I don't think so - I don't think adding a try block has any overhead unless an exception is actually thrown. But I'm not sure, I guess we could try to gather some empirical evidence.

qualidafial Fri 5 Jun 2009

I've run a benchmark with the following code:

public void testIsSpace_TryCatch() {
  runBenchmark( "isSpace_TryCatch", new Runnable() {
    int count;

    public void run() {
      for ( int i = 0; i < Integer.MAX_VALUE; i++ )
        if ( isSpace_TryCatch( i & 0x7F ) )
          count++;
    }
  } );
}

public static boolean isSpace_TryCatch( long self ) {
  try {
    return ( self < 128 && ( charMap[(int) self] & SPACE ) != 0 );
  }
  catch ( ArrayIndexOutOfBoundsException e ) {
    // should be very rare to use this method with negative
    // numbers, so don't take the hit every call
    return false;
  }
}

public void testIsSpace_IfClause() {
  runBenchmark( "isSpace_IntCompare", new Runnable() {
    int count;

    public void run() {
      for ( int i = 0; i < Integer.MAX_VALUE; i++ )
        if ( isSpace_IntCompare( i & 0x7F ) )
          count++;
    }
  } );
}

public static boolean isSpace_IntCompare( long self ) {
  return ( self < 128 && self >= 0 && ( charMap[(int) self] & SPACE ) != 0 );
}

Note that the int count stuff had to be added to keep the VM from optimizing away the isSpace method calls.

The results:

Benchmarking 'isSpace_TryCatch':
	Run 1/10:	3577ms
	Run 2/10:	3521ms
	Run 3/10:	3526ms
	Run 4/10:	3494ms
	Run 5/10:	3498ms
	Run 6/10:	3505ms
	Run 7/10:	3495ms
	Run 8/10:	3499ms
	Run 9/10:	3498ms
	Run 10/10:	3501ms
Total:  	35114ms
Average:	3,511.4ms

Benchmarking 'isSpace_IntCompare':
	Run 1/10:	3510ms
	Run 2/10:	3537ms
	Run 3/10:	3507ms
	Run 4/10:	3508ms
	Run 5/10:	3523ms
	Run 6/10:	3524ms
	Run 7/10:	3533ms
	Run 8/10:	3501ms
	Run 9/10:	3505ms
	Run 10/10:	3500ms
Total:  	35148ms
Average:	3,514.8ms

So it appears that while my previous concern about try/catch affecting performance is moot, so is the stated reasoning for using a try/catch block in the first place. It just doesn't make a difference either way.

The benchmark utility I use is a home-grown one which I'm happy to share if you're curious or if you want to reproduce these results on your machine.

qualidafial Fri 5 Jun 2009

I ran the benchmark again, this time using java -Xint to eliminate VM optimizations. I had to modify the test to make it run in a reasonable time:

private static int COUNT = 1 << 24;

public void testIsSpace_TryCatch() {
  runBenchmark( "isSpace_TryCatch", new Runnable() {
    public void run() {
      for ( int i = 0; i < COUNT; i++ )
        isSpace_TryCatch( 17 );
    }
  } );
}

public static boolean isSpace_TryCatch( long self ) {
  // same as before
}

public void testIsSpace_IfClause() {
  runBenchmark( "isSpace_IntCompare", new Runnable() {
    public void run() {
      for ( int i = 0; i < COUNT; i++ )
        isSpace_IntCompare( 17 );
    }
  } );
}

public static boolean isSpace_IntCompare( long self ) {
  // same as before
}

Results:

Benchmarking 'isSpace_TryCatch':
	Run 1/10:	1743ms
	Run 2/10:	1732ms
	Run 3/10:	1749ms
	Run 4/10:	1746ms
	Run 5/10:	1738ms
	Run 6/10:	1734ms
	Run 7/10:	1734ms
	Run 8/10:	1750ms
	Run 9/10:	1750ms
	Run 10/10:	1740ms
Total:  	17416ms
Average:	1,741.6ms

Benchmarking 'isSpace_IntCompare':
	Run 1/10:	1831ms
	Run 2/10:	1824ms
	Run 3/10:	1828ms
	Run 4/10:	1828ms
	Run 5/10:	1826ms
	Run 6/10:	1827ms
	Run 7/10:	1828ms
	Run 8/10:	1828ms
	Run 9/10:	1827ms
	Run 10/10:	1829ms
Total:  	18276ms
Average:	1,827.6ms

So the int comparison actually does take about 5% longer compared to using a try / catch block, in interpreted mode.

brian Sat 6 Jun 2009

Interesting. Thanks for running those benchmarks.

Login or Signup to reply.