Community
Participate
Working Groups
Linux RH9, tested with default locale en_US.UTF8 and the japanese locale I20030611, hotspot 1.4.1_02 Some methods in the Scanner class generate a significant number of java.lang.IndexOutOfBoundsException when switching between 2 java editors. Use case: switch between java editors .OptimizeIt reports about 17% of the CPU time of the org.eclipse.jdt.internal.ui.text.JavaReconciler thread is spent throwing IndexOutOfBoundsException instances. This represents about 9% of the time of all threads in the main thread group, and appears to be the most significant hotspot for this use case. The tool reports these exceptions are generated from the following methods inside Scanner: getNextChar, getNextToken, getNextCharAsDigit .Without OptimizeIt, I instrumented the 2 methods getNextChar to count these IndexOutOfBoundsException. About 2000 IndexOutOfBoundsException instances were thrown when switching from one java file to another. The java files I used are org.eclipse.swt.widgets.Shell and org.eclipse.swt.graphics.Cursor from SWT GTK. An older version of Eclipse does not throw these exceptions on Windows. This may be because my Windows box and my Linux box use different encoding or/and that something has changed in the Scanner code. If this was not expected and can be fixed, this will contribute to improving the time taken to switch between java editors.
Moving to JDT/Core.
The scanner indeed doesn't check for boundaries eagerly and rather rely on the fact that there is only one such failure at the end of a unit. Need to investigate this scenario, since it may occur more than we initially thought.
Ran the following test on 3 separate machines of varying speeds. Expect to see the break even point between catching an exception & performing a bounds check to be between 1000-2500 array accesses. If you can be sure that your code will throw an IndexOutOfBounds exception every few hundred accesses then you should definitely do a bounds check... but if its every 5-10,000 accesses or more then catching the exception is more efficient. public class WithException { static int LoopSize = 100000; public static void main(String[] args) { int[] arraySizes = new int[] {500, 1000, 2500, 5000}; for (int i = 0, l = arraySizes.length; i < l; i++) { int[] numbers = new int[arraySizes[i]]; int numberOfRuns = 3; long t = 0; for (int n = numberOfRuns; --n >= 0; ) t += catchException(numbers); System.out.println("catchException " + numbers.length + " -> " + t / numberOfRuns); t = 0; for (int n = numberOfRuns; --n >= 0; ) t += performCheck(numbers); System.out.println("performCheck " + numbers.length + " -> " + t / numberOfRuns); } } public static long catchException(int[] numbers) { long t = System.currentTimeMillis(); for (int i = LoopSize; --i >= 0; ) { try { for (int j = 0, l = 2 * numbers.length; j < l; j++) numbers[j] = 1; } catch (IndexOutOfBoundsException ignore) { for (int j = 0, l = numbers.length; j < l; j++) numbers[j] = 1; } } return System.currentTimeMillis() - t; } public static long performCheck(int[] numbers) { long t = System.currentTimeMillis(); for (int i = 2 * LoopSize; --i >= 0; ) for (int j = 0, l = numbers.length; j < l; j++) if (j < l) numbers[j] = 1; return System.currentTimeMillis() - t; } }
Spawned the CodeStream & ConstantPool (which account for 300 of 341 cases) to bug 62131.
I released some changes to the Scanner so that does bounds checks for the common cases.
Did you copy the internal scanner inside the PublicScanner?
Yes the PublicScanner was updated. Released the remaining changes. The CodeStream & ConstantPool will be taken care of by bug 62131.
Verified in 200405281200